如何在 sh 脚本中从“\033[6n”获取结果

如何在 sh 脚本中从“\033[6n”获取结果

我花了 10 个小时在网络上搜索并测试技术以获得适用于任何 shell 的结果 ( #!/bin/sh)。

在 BASH 中这相对简单,因为read可以知道要抓取多少个字符,并且如果找到分隔符,它会立即退出。

stty -icanon -echo; echo -en "\033[6n"; read -d R -n 12 ESCPOS; stty "$x_TERM"; \
ESCPOS=`echo "$ESCPOS" | tail -c +3`; echo "$ESCPOS"

如何编写一个兼容任何shell的sh脚本版本?

答案1

复制自https://unix.stackexchange.com/a/88327

假设您/bin/sh是 POSIX sh(在 Solaris 10 及更早版本上,那里有 Bourne shell,请改用/usr/xpg4/bin/sh):

if [ -t 0 ] && [ -t 1 ]; then
  old_settings=$(stty -g) || exit
  stty -icanon -echo min 0 time 3 || exit
  printf '\033[6n'
  pos=$(dd count=1 2> /dev/null)
  pos=${pos%R*}
  pos=${pos##*\[}
  x=${pos##*;} y=${pos%%;*}
  stty "$old_settings"
fi

假设整个响应将一次性等待最多 0.3 秒。对于终端仿真器和 pty 设备来说通常应该如此,但对于串行终端则不一定。您可以将其更改为min 8 time 3继续等待(每个字节之间最多 0.3 秒),直到读取 8 个字节,但缺点是,如果答案短于 8 个字节,则始终需要至少 0.3 秒,并且如果没有答案)。

您可以awk -F'[^0-9]+' -v RS=R '{print $3, $2; exit}'与 一起使用min 1 time 0。这可以awk与其他实现一起使用mawk(它坚持在开始处理输入之前累积一个充满输入数据的缓冲区)。

最后,像您自己的答案一样一次读取一个字节是最可靠的。您可能需要添加超时来考虑不发送响应的终端。

答案2

笔记:

unlike the wrongly copied and continually upvoted answer provided
(points scoring?), the following script IS NON-BLOCKING, and does
not care what length returned input may be.  IE it will work with 
ANY screen size.

使用 SH 则更复杂,并且我无法找到内置的增强命令行版本read,最终我在 STDIN 上找到了提及dd,这是结果。请注意,内置的 SH 版本echo不允许使用,echo -en尽管/bin/echo -en我们可以使用printf它。

#!/bin/sh
x_TERM=`stty -g`
stty -icanon -echo
printf "\033[6n"
ESCPOS=""
X=""
I=0
while [ ! "$X" = "R" ]; do
  X=`dd bs=1 count=1 2>/dev/null`
  I=`expr $I + 1`
  if [ $I -gt 2 -a ! "$X" = "R" ]; then
    ESCPOS="$ESCPOS$X"
  fi
done
stty "$x_TERM"
#echo "$ESCPOS"
CSRLIN=`echo "$ESCPOS" | cut -d \; -s -f 1`
POS=`echo "$ESCPOS" | cut -d \; -s -f 2`
echo "$CSRLIN"
#exit 0 <= dont use inline

我在两个不同的脚本中使用了相同的代码,一个是输出CSRLIN,另一个是输出POS

编辑:您需要内联此脚本才能在另一个脚本中使用它(例如. CSRLIN,因为 shell 必须处于交互模式。

干杯

保罗

相关内容