Como obter entrada de caixa de diálogo direcionada para uma variável?

15

Eu venho me ensinando scripts bash e me deparei com um problema. Eu escrevi um script para receber a entrada do usuário, usando o comando 'read', e fazer dessa entrada uma variável para usar posteriormente no script. O script funciona, mas ....

Eu gostaria de poder configurá-lo usando 'dialog'. Eu descobri que

'dialog --inputbox' irá direcionar a saída para 'stderr' e para obter essa entrada como uma variável você deve direcioná-la para um arquivo e depois recuperá-la. O código que encontrei para explicar isso é:

#!/bin/bash
dialog --inputbox \

"What is your username?" 0 0 2> /tmp/inputbox.tmp.$$

retval=$?

input='cat /tmp/inputbox.tmp.$$'

rm -f /tmp/inputbox.tmp.$$

case $retval in
0)

echo "Your username is '$input'";;
1)

echo "Cancel pressed.";;

esac

Eu vejo que ele está enviando o sdterr para o /tmp/inputbox.tmp.$$ com 2 & gt ;, mas o arquivo de saída se parece com 'inputbox.tmp.21661'. Quando tento catar o arquivo, isso me causa um erro. Portanto, ainda não consigo obter a entrada do usuário da --inputbox como uma variável.

Exemplo de script:

echo "  What app would you like to remove? "

read dead_app

sudo apt-get remove --purge $dead_app

Então, como você pode ver, é um script básico. É possível obter a variável como uma palavra de dialog --inputbox ?

    
por emerikanbloke 04.07.2014 / 04:19

6 respostas

15

  

: D Eu não posso explicar isso !!! Se você puder entender o que eles estão dizendo no Guia Avançado de Roteiro de Bash: Capítulo 20. Redirecionamento de I / O , escreva uma nova resposta e eu lhe darei 50rep :

exec 3>&1;
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
exitcode=$?;
exec 3>&-;
echo $result $exitcode;
     

Referência: O diálogo no bash é Não pegar variáveis corretamente

^ resposta de @Sneetsher (4 de julho de 2014)

Conforme solicitado, tentarei explicar o que este snippet está fazendo linha por linha.

Note que eu simplificarei isso omitindo todos os ; ponto e vírgula nas extremidades da linha, porque eles não são necessários se escrevermos um comando por linha.

E / S - fluxos:

Primeiro, você precisa entender os fluxos de comunicação. Existem 10 fluxos, numerados de 0 a 9:

  • Fluxo 0 ("STDIN"):
    "Entrada padrão", o fluxo de entrada padrão para ler dados do teclado.

  • Stream 1 ("STDOUT"):
    "Saída padrão", o fluxo de saída padrão usado para mostrar texto normal no terminal.

  • Fluxo 2 ("STDERR"): "Erro padrão", o fluxo de saída padrão usado para exibir erros ou outro texto para fins especiais no terminal.

  • Fluxos 3-9:
    Fluxos adicionais, livremente utilizáveis. Eles não são usados por padrão e não existem até que algo tente usá-los.

Observe que todos os "fluxos" são representados internamente pelos descritores de arquivos em /dev/fd (que é um link simbólico para /proc/self/fd que contém outro link simbólico para cada fluxo ... é um pouco complicado e não é importante para o comportamento deles Então eu paro aqui. Os fluxos padrão também têm /dev/stdin , /dev/stdout e /dev/stderr (que são links simbólicos novamente, etc ...).

O script:

  • exec 3>&1
    

    O Bash built-in exec pode ser usado para aplicar um redirecionamento de fluxo ao shell, o que significa que ele afeta todos os comandos a seguir. Para mais informações, execute help exec no seu terminal.

    Neste caso especial, o fluxo 3 é redirecionado para o fluxo 1 (STDOUT), o que significa que tudo que enviamos para o fluxo 3 mais tarde aparecerá em nosso terminal como se fosse normalmente impresso em STDOUT.

  • result=$(dialog --inputbox test 0 0 2>&1 1>&3)
    

    Esta linha consiste em muitas partes e estruturas sintáticas:

    • result=$(...)
      Essa estrutura executa o comando entre colchetes e atribui a saída (STDOUT) à variável bash result . É legível através de $result . Tudo isso é descrito de alguma forma na veeeery looong man bash .

    • dialog --inputbox TEXT HEIGHT WIDTH
      Este comando mostra uma caixa de TUI com o TEXTO dado, um campo de entrada de texto e dois botões OK e CANCELAR. Se OK for selecionado, o comando sai com o status 0 e imprime o texto digitado em STDERR, se CANCEL for selecionado, sairá com o código 1 e não imprimirá nada. Para mais informações, leia man dialog .

    • 2>&1 1>&3
      Estes são dois comandos de redirecionamento. Eles serão interpretados da direita para a esquerda:

      1>&3 redireciona o fluxo do comando 1 (STDOUT) para o fluxo personalizado 3.

      2>&1 redireciona depois o fluxo do comando 2 (STDERR) para o fluxo 1 (STDOUT).

      Isso significa que tudo o que o comando imprime no STDOUT aparece agora no fluxo 3, enquanto tudo o que deveria ser exibido no STDERR agora é redirecionado para o STDOUT.

    Assim, a linha inteira exibe um prompt de texto (no STDOUT, que foi redirecionado para o fluxo 3, que o shell redireciona novamente para STDOUT no final - veja o comando exec 3>&1 ) e atribui os dados inseridos (retornados pelo STDERR , em seguida, redirecionado para STDOUT) para a variável de bash result .

  • exitcode=$?
    

    Este código recupera o código de saída do comando executado anteriormente (aqui de dialog ) através da variável reservada Bash $? (sempre mantém o último código de saída) e simplesmente o armazena em nossa própria variável Bash exitcode . Pode ser lido por $exitcode novamente. Você pode pesquisar mais informações sobre isso em man bash , mas isso pode demorar um pouco ...

  • exec 3>&-
    

    O Bash built-in exec pode ser usado para aplicar um redirecionamento de fluxo ao shell, o que significa que ele afeta todos os comandos a seguir. Para mais informações, execute help exec no seu terminal.

    Neste caso especial, o stream 3 é redirecionado para "stream -", o que significa que deve ser fechado. Os dados enviados para o stream 3 não serão redirecionados para mais lugar algum a partir de agora.

  • echo $result $exitcode
    

    Este simples comando echo (mais informações sobre man echo ) apenas imprime o conteúdo das duas variáveis de Bash result e exitcode para o STDOUT. Como não temos mais nenhum redirecionamento de fluxo explícito ou implícito aqui, eles realmente aparecerão no STDOUT e, portanto, simplesmente serão exibidos no terminal. Que milagre!; -)

Resumo:

Primeiro, configuramos o shell para redirecionar tudo que enviamos para o fluxo customizado 3 de volta para STDOUT, para que ele apareça em nosso terminal.

Em seguida, executamos o comando dialog , redirecionamos seu STDOUT original para nosso fluxo personalizado 3, porque ele precisa ser exibido no final, mas precisamos usar temporariamente o fluxo STDOUT para outra finalidade.
Nós redirecionamos o STDERR original do comando, onde a entrada do usuário da janela de diálogo é retornada, para STDOUT depois.
Agora podemos capturar o STDOUT (que contém os dados redirecionados do STDERR) e armazená-lo em nossa variável $result . Ele contém a entrada do usuário desejada agora!

Também queremos o código de saída do comando dialog , que mostra se OK ou CANCEL foi clicado. Este valor é apresentado na variável Bash reservada $? e nós apenas copiamos para nossa variável $exitcode .

Depois disso, fechamos o stream 3 novamente, já que não precisamos mais dele, para impedir mais redirecionamentos dele.

Por fim, normalmente emitimos o conteúdo de ambas as variáveis $result (a entrada do usuário da janela de diálogo) e $exitcode (0 para OK, 1 para CANCEL) no terminal.

    
por Byte Commander 01.12.2015 / 15:28
8

Se você ler man page for dialog, existe a opção --output-fd , que permite definir explicitamente para onde a saída vai (STDOUT 1, STDERR 2), em vez de, por padrão, ir para STDERR.

Abaixo, você pode me ver executando o comando dialog da amostra, explicitamente afirmando que a saída deve ir para o descritor de arquivo 1, que me permite salvá-lo em MYVAR.

MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)

Uma abordagem alternativa que tem muito potencial oculto é usar algo conhecido como canal nomeado .

xieerqi: $ cat dialog_with_npipe.sh

#!/bin/bash

mkfifo /tmp/namedPipe1 # this creates named pipe, aka fifo

dialog --inputbox "This is an input box  with named pipe" 40 40 2> /tmp/namedPipe1 & # to make sure the shell doesn't hang, we run redirection in background, because fifo waits for output to come out

OUTPUT="$( cat /tmp/namedPipe1  )" # release contents of pipe

echo  "This is the output " $OUTPUT

    
por Sergiy Kolodyazhnyy 01.12.2015 / 16:47
6

: D Eu não posso explicar isso !!! Se você puder entender o que eles estão dizendo na referência: Guia Avançado de Roteiro de Bash: Capítulo 20. I / O Redirecionamento , escreva uma nova resposta e eu lhe darei 50rep

Recompensa foi dada, para explicação veja resposta do ByteCommander . :) Esta é uma parte da história.

exec 3>&1;
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
exitcode=$?;
exec 3>&-;
echo $result $exitcode;

Fonte: O diálogo no bash é não pegar variáveis corretamente
Referência: Bash avançado -Guia de script: Capítulo 20. Redirecionamento de E / S

    
por user.dz 04.07.2014 / 15:57
4

Caso alguém também tenha chegado do Google, e embora essa pergunta seja específica para o bash, aqui está outra alternativa:

Você pode usar o zenity . O Zenity é um utilitário gráfico que pode ser usado dentro de scripts bash. Mas é claro que isso exigiria um servidor X, como user877329 corretamente apontou.

sudo apt-get install zenity

Em seguida, no seu script:

RETVAL='zenity --entry --title="Hi" --text="What is your username"'

Link útil .

    
por Wtower 01.12.2015 / 13:49
3

A resposta fornecida por Sneetsher é um pouco mais elegante, mas posso explicar o que está errado: O valor de $$ é diferente dentro dos backticks (porque ele inicia um novo shell e $$ é o PID do shell atual ). Você vai querer colocar o nome do arquivo em uma variável e, em vez disso, se referir a essa variável.

#!/bin/bash
t=$(mktemp -t inputbox.XXXXXXXXX) || exit
trap 'rm -f "$t"' EXIT         # remove temp file when done
trap 'exit 127' HUP STOP TERM  # remove if interrupted, too
dialog --inputbox \
    "What is your username?" 0 0 2>"$t"
retval=$?
input=$(cat "$t")  # Prefer $(...) over '...'
case $retval in
  0)    echo "Your username is '$input'";;
  1)    echo "Cancel pressed.";;
esac

Nesse caso, evitar o arquivo temporário seria uma solução melhor, mas haverá muitas situações em que você não poderá evitar um arquivo temporário.

    
por tripleee 01.12.2015 / 14:04
3

Isso funciona para mim:

#!/bin/bash
input=$(dialog --stdout --inputbox "What is your username?" 0 0)
retval=$?

case $retval in
${DIALOG_OK-0}) echo "Your username is '$input'.";;
${DIALOG_CANCEL-1}) echo "Cancel pressed.";;
${DIALOG_ESC-255}) echo "Esc pressed.";;
${DIALOG_ERROR-255}) echo "Dialog error";;
*) echo "Unknown error $retval"
esac

A página de manual de dialog informa sobre --stdout:

  

Saída direta para a saída padrão. Esta opção é fornecida para compatibilidade com o Xdialog, no entanto, não é recomendável usá-lo em scripts portáteis, uma vez que os curses normalmente gravam suas atualizações de tela na saída padrão. Se você usar essa opção, o diálogo tentará reabrir o terminal para que ele possa gravar no visor. Dependendo da plataforma e do seu ambiente, isso pode falhar.

Alguém pode dizer em qual plataforma ou ambiente não funciona? O redirecionamento da dialog output para 2>&1 >/dev/tty funciona melhor então?

    
por jarno 08.08.2016 / 10:43

Tags