我花了 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 必须处于交互模式。
干杯
保罗