Como obter os resultados de “\ 033 [6n” em um script sh

0

Levei 10 horas pesquisando na rede e testando técnicas para obter os resultados que funcionavam em qualquer shell ( #!/bin/sh ).

No BASH isso é relativamente simples, porque read pode ser informado de quantos caracteres devem ser pegos, e se um delimitador for encontrado, ele não irá esperar para sair.

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

Como escrever uma versão do script sh, compatível com qualquer shell?

    
por Paul Wratt 27.08.2017 / 06:40

2 respostas

2

Nota:

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.

Com o SH, ele é mais complexo e não consegui encontrar a versão de linha de comando aprimorada do read incorporado; eventualmente, encontrei uma menção de dd no STDIN; aqui está o resultado. OBSERVE que a versão SH do echo integrado não permite o uso echo -en , embora /bin/echo -en funcione, usamos printf .

#!/bin/sh
x_TERM='stty -g'
stty -icanon -echo
printf "3[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

Eu usei o mesmo código em dois scripts diferentes, um gera CSRLIN , o outro POS .

EDITAR: você precisa embutir este script para usá-lo em outro script (por exemplo, . CSRLIN , já que o shell deve estar no modo interativo.

Felicidades

Paul

    
por 27.08.2017 / 06:40
4

Copiado de link

Assumindo que seu /bin/sh é um POSIX sh (no Solaris 10 e mais antigo que tinha um shell Bourne, use /usr/xpg4/bin/sh ):

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

Isso pressupõe que toda a resposta virá de uma só vez esperando por até 0,3 segundos. Isso deve ser geralmente verdadeiro para emuladores de terminal e dispositivos pty, mas não necessariamente para terminais em série. Você pode alterá-lo para min 8 time 3 para continuar esperando (até 0,3 segundo entre cada byte) até que 8 bytes tenham sido lidos, mas com a desvantagem de que levará sempre pelo menos 0,3 segundos se a resposta for menor que 8 bytes e pendure para sempre se não houver resposta).

Você pode usar awk -F'[^0-9]+' -v RS=R '{print $3, $2; exit}' com min 1 time 0 . Isso funcionaria com awk implementações diferentes de mawk (que insiste em acumular um buffer cheio de dados na entrada antes de começar a processá-lo).

No final, ler um byte de cada vez, como você faz em sua própria resposta, é o mais confiável. Você pode adicionar um tempo limite para os terminais que não enviarem uma resposta.

    
por 27.08.2017 / 08:36