Eu tenho me ensinado a bash scripts e me deparei com um problema. Eu escrevi um script para receber a entrada do usuário, usando o comando 'read', e faço dessa entrada uma variável para usar posteriormente no script. O script funciona, mas ....
Gostaria de conseguir configurá-lo usando o 'diálogo'. Eu descobri que
'dialog --inputbox' direcionará a saída para 'stderr' e, para obter essa entrada como uma variável, você deve direcioná-la para um arquivo e 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
Vejo que ele está enviando o sdterr para o /tmp/inputbox.tmp.$$ com 2>, mas o arquivo de saída se parece com 'inputbox.tmp.21661'. Quando tento capturar o arquivo, ocorre um erro. Portanto, ainda não consigo obter a entrada do usuário da --inputbox como uma variável.
Script de exemplo:
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 dialog --inputbox
?
mktemp
comando para criar um arquivo temporário.Respostas:
^ resposta de @Sneetsher (4 de julho de 2014)
Conforme solicitado, tentarei explicar o que esse snippet está fazendo linha por linha.
Observe que simplificarei omitindo todos os
;
pontos e vírgulas 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.
Fluxo 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 arquivo em
/dev/fd
(que é um link simbólico para o/proc/self/fd
qual contém outro link simbólico para cada fluxo ... é um pouco complicado e não é importante para o comportamento deles, então 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:
O Bash interno
exec
pode ser usado para aplicar um redirecionamento de fluxo ao shell, o que significa que afeta todos os seguintes comandos. Para mais informações, corrahelp exec
no seu terminal.Nesse caso especial, o fluxo 3 é redirecionado para o fluxo 1 (STDOUT), ou seja, tudo o que enviamos ao fluxo 3 mais tarde aparecerá em nosso terminal como se fosse normalmente impresso em STDOUT.
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$result
. Tudo isso é descrito de alguma forma no muito obscuroman bash
.dialog --inputbox TEXT HEIGHT WIDTH
Este comando mostra uma caixa TUI com o TEXTO fornecido, um campo de entrada de texto e dois botões OK e CANCEL. Se OK for selecionado, o comando sairá com o status 0 e imprimirá o texto digitado em STDERR; se CANCEL for selecionado, ele 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 1 do comando (STDOUT) para o fluxo personalizado 3.2>&1
redireciona depois o fluxo 2 do comando (STDERR) para o fluxo 1 (STDOUT).Isso significa que tudo o que o comando imprime no STDOUT agora aparece no fluxo 3, enquanto tudo o que se destinava a aparecer no STDERR agora é redirecionado para o STDOUT.
Portanto, a linha inteira exibe um prompt de texto (no STDOUT, que foi redirecionado para o fluxo 3, que o shell redireciona novamente para o STDOUT no final - veja o
exec 3>&1
comando) e atribui os dados inseridos (retornados por STDERR e depois redirecionados para STDOUT) para a variável Bashresult
.Esse código recupera o código de saída do comando executado anteriormente (daqui de
dialog
) através da variável Bash reservada$?
(sempre mantém o último código de saída) e simplesmente o armazena em nossa própria variável Bashexitcode
. Pode ser lido$exitcode
novamente. Você pode procurar mais informações sobre isso emman bash
, mas isso pode demorar um pouco ...O Bash interno
exec
pode ser usado para aplicar um redirecionamento de fluxo ao shell, o que significa que afeta todos os seguintes comandos. Para mais informações, corrahelp exec
no seu terminal.Nesse caso especial, o fluxo 3 é redirecionado para "fluxo -", o que significa apenas que deve ser fechado. Os dados enviados para o fluxo 3 não serão mais redirecionados a partir de agora.
Este
echo
comando simples (mais informaçõesman echo
) apenas imprime o conteúdo das duas variáveis Bashresult
eexitcode
no STDOUT. Como não temos mais redirecionamentos explícitos ou implícitos de stream aqui, eles realmente aparecerão no STDOUT e, portanto, serão exibidos no terminal. Que milagre! ;-)Resumo:
Primeiro, configuramos o shell para redirecionar tudo o que enviamos ao fluxo personalizado 3 de volta ao STDOUT, para que ele apareça em nosso terminal.
Em seguida, executamos o
dialog
comando, redirecionamos seu STDOUT original para nosso fluxo personalizado 3, porque ele precisa ser exibido no final, mas precisamos temporariamente usar o fluxo STDOUT para outra coisa.Nós redirecionamos o STDERR original do comando, onde a entrada do usuário da janela de diálogo é retornada, para STDOUT posteriormente.
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 de usuário desejada agora!Também queremos o
dialog
código de saída do comando, que mostra se OK ou CANCEL foi clicado. Este valor é apresentado na variável Bash reservada$?
e apenas o copiamos para nossa própria variável$exitcode
.Depois disso, fechamos o fluxo 3 novamente, pois não precisamos mais dele, para impedir novos redirecionamentos.
Finalmente, 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) para o terminal.fonte
exec
é desnecessariamente complicado. Por que não apenas--stdout
optar por nósdialog
ou redirecionar sua saída2>&1 >/dev/tty
?Usando as próprias ferramentas da caixa de diálogo: - sinalizador de saída-fd
Se você ler a página de manual para o diálogo, 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
dialog
comando de amostra , afirmando explicitamente que a saída deve ir para o descritor de arquivo 1, o que me permite salvá-la no MYVAR.MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Usando pipes nomeados
Uma abordagem alternativa que tem muito potencial oculto é usar algo conhecido como pipe nomeado .
Uma visão geral mais detalhada da resposta do user.dz com abordagem alternativa
A resposta original de user.dz e a explicação de ByteCommander fornecem uma boa solução e uma visão geral do que ele faz. No entanto, acredito que uma análise mais profunda pode ser benéfica para explicar por que funciona.
Antes de tudo, é importante entender duas coisas: qual é o problema que estamos tentando resolver e quais são os trabalhos subjacentes dos mecanismos shell com os quais estamos lidando. A tarefa é capturar a saída de um comando via substituição de comando. Sob uma visão geral simplista que todos sabem, as substituições de comando capturam o
stdout
comando e permitem que ele seja reutilizado por outra coisa. Nesse caso, aresult=$(...)
peça deve salvar a saída de qualquer comando designado por...
uma variável chamadaresult
.Debaixo do capô, a substituição de comando é realmente implementada como canal, onde há um processo filho (o comando real que é executado) e um processo de leitura (que salva a saída em variável). Isso é evidente com um rastreamento simples de chamadas do sistema. Observe que o descritor de arquivo 3 é o final da leitura do canal, enquanto 4 é o final da gravação. Para o processo filho de
echo
, que grava no seustdout
- o descritor de arquivo 1, esse descritor de arquivo é na verdade cópia do descritor de arquivo 4, que é o final da gravação do pipe. Observe questderr
não está desempenhando um papel aqui, simplesmente porque éstdout
apenas um tubo conectado .Vamos voltar à resposta original por um segundo. Como agora sabemos que
dialog
a caixa TUI é gravadastdout
, respondidastderr
e dentro da substituição de comandostdout
é canalizada para outro lugar, já temos parte da solução - precisamos reconectar os descritores de arquivo de maneira questderr
sejam canalizados para o processo do leitor. Esta é a2>&1
parte da resposta. No entanto, o que fazemos com a caixa TUI?É aí que entra o descritor de arquivo 3. O
dup2()
syscall nos permite duplicar descritores de arquivo, fazendo com que eles se refiram efetivamente ao mesmo local, mas podemos manipulá-los separadamente. Os descritores de arquivo dos processos que possuem o terminal de controle conectado realmente apontam para um dispositivo de terminal específico. Isso é evidente se você fizeronde
/dev/pts/5
está meu dispositivo pseudo-terminal atual. Assim, se, de alguma forma, podemos salvar esse destino, ainda podemos escrever a caixa TUI na tela do terminal. Isso é o queexec 3>&1
faz. Quando você chama um comando com redirecionamento,command > /dev/null
por exemplo, o shell passa seu descritor de arquivo stdout e, em seguida, usadup2()
para gravar esse descritor de arquivo/dev/null
. Oexec
comando executa algo semelhante aosdup2()
descritores de arquivo para toda a sessão do shell, fazendo com que qualquer comando herde o descritor de arquivo já redirecionado. O mesmo comexec 3>&1
. O descritor de arquivo3
agora fará referência a / apontar para o terminal de controle e qualquer comando executado nessa sessão de shell saberá sobre ele.Então, quando
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
ocorre, o shell cria um canal para a caixa de diálogo ser gravada, mas também2>&1
primeiro faz com que o descritor de arquivo do comando 2 seja duplicado no descritor de arquivo de gravação desse canal (fazendo com que a saída vá para a extremidade final do canal e para a variável) , enquanto o descritor de arquivo 1 será duplicado em 3. Isso fará com que o descritor de arquivo 1 ainda se refira ao terminal de controle, e a caixa de diálogo TUI será exibida na tela.Agora, na verdade, existe uma mão curta para o atual terminal de controle do processo, que é
/dev/tty
. Assim, a solução pode ser simplificada sem o uso de descritores de arquivo, simplesmente em:Coisas importantes a lembrar:
Veja também
fonte
--stdout
opção pode ser perigosa e falhar facilmente em alguns sistemas, e acho que--output-fd 1
está fazendo o mesmo:--stdout: Direct output to the standard output. This option is provided for compatibility with Xdialog, however using it in portable scripts is not recommended, since curses normally writes its screen updates to the standard output. If you use this option, dialog attempts to reopen the terminal so it can write to the display. Depending on the platform and your environment, that may fail.
- No entanto, a ideia de pipe nomeado é legal!--output-fd
, que é a opção que usei aqui, não--stdout
. Segundo, o diálogo está sendo desenhado primeiro no stdout, a saída retornada é o segundo. Nós não fazemos essas duas coisas ao mesmo tempo. No entanto,--output-fd
não requer especificamente o uso de fd 1 (STDOUT). Ele pode ser facilmente redirecionado para outro descritor de arquivo #: DI não pode explicar !!! Se você puder entender o que eles estão dizendo na referência: Guia Avançado de Scripts em Bash: Capítulo 20. Redirecionamento de E / S , escreva uma nova resposta e eu darei 50repRecompensa foi dada, para obter explicação, consulte a resposta do ByteCommander . :) Isso faz parte da história.
Fonte: Diálogo no bash não está capturando variáveis corretamente
Referência: Guia Avançado de Scripts Bash: Capítulo 20. Redirecionamento de E / S
fonte
Isso funciona para mim:
A página de manual
dialog
fala sobre --stdout:Alguém pode dizer em qual plataforma ou ambiente ele não funciona? O redirecionamento de
dialog
saída para o2>&1 >/dev/tty
trabalho funciona melhor então?fonte
Caso outra pessoa tenha chegado aqui do Google e, embora essa pergunta solicite especificamente 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 indicado por user877329.
Então no seu script:
Link útil .
fonte
dialog
. É como se eu perguntasse "Como escrevo isso e aquilo em python?", Mas você me dá uma festinha - estou muito feliz que isso possa ser feito de maneira diferente, mas não é isso que estou perguntandoA 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ê desejará colocar o nome do arquivo em uma variável e, em vez disso, consulte essa variável.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.
fonte