Come ottenere l'input della finestra di dialogo diretto a una variabile?


18

Mi sono insegnato a scrivere script e ho riscontrato un problema. Ho scritto uno script per prendere l'input dall'utente, usando il comando 'read', e rendere quell'input una variabile da usare in seguito nello script. La sceneggiatura funziona, ma ....

Vorrei poterlo configurare usando 'dialog'. ho scoperto che

'dialog --inputbox' indirizzerà l'output su 'stderr' e per ottenere quell'input come variabile devi dirigerlo su un file e poi recuperarlo. Il codice che ho trovato per spiegare questo è:

#!/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

Vedo che sta inviando sdterr a /tmp/inputbox.tmp.$$ con 2>, ma il file di output sembra 'inputbox.tmp.21661'. Quando provo a cat il file mi dà un errore. Quindi non sono ancora in grado di ottenere l'input dell'utente da --inputbox come variabile.

Script di esempio:

echo "  What app would you like to remove? "

read dead_app

sudo apt-get remove --purge $dead_app

Come puoi vedere, è uno script di base. È anche possibile ottenere la variabile come parola da dialog --inputbox?


Nella mia esperienza lo script funziona bene, se rimuovi la riga vuota dopo la 2a riga. In alternativa, è possibile utilizzare il mktempcomando per creare un file temporaneo.
jarno,

Risposte:


16

: DI non posso spiegarlo !!! Se riesci a capire cosa stanno dicendo in Advanced Bash-Scripting Guide: Chapter 20. I / O Redirection , scrivi una nuova risposta e ti darò 50rep :

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

Riferimento: la finestra di dialogo in bash non sta acquisendo correttamente le variabili

^ risposta di @Sneetsher (4 lug 2014)

Come richiesto, cercherò di spiegare cosa sta facendo questo frammento riga per riga.

Nota che lo semplificherò omettendo tutti i ;punti e virgola all'estremità della riga, perché non sono necessari se scriviamo un comando per riga.

I / O - Stream:

Innanzitutto, è necessario comprendere i flussi di comunicazione. Esistono 10 flussi, numerati da 0 a 9:

  • Stream 0 ("STDIN"):
    "Input standard", lo stream di input predefinito per leggere i dati dalla tastiera.

  • Stream 1 ("STDOUT"):
    "Output standard", lo stream di output predefinito utilizzato per mostrare il testo normale nel terminale.

  • Stream 2 ("STDERR"): "Errore standard", il flusso di output predefinito utilizzato per visualizzare errori o altro testo a scopi speciali nel terminale.

  • Stream 3-9: stream
    aggiuntivi, liberamente utilizzabili. Non vengono utilizzati per impostazione predefinita e non esistono fino a quando qualcosa non tenta di usarli.

Nota che tutti gli "stream" sono rappresentati internamente dai descrittori di file in /dev/fd(che è un link simbolico al /proc/self/fdquale contiene un altro link simbolico per ogni stream ... è un po 'complicato e non importante per il loro comportamento, quindi mi fermo qui.). Anche i flussi standard hanno /dev/stdin, /dev/stdoute /dev/stderr(che sono di nuovo collegamenti simbolici, ecc ...).

Il copione:

  • exec 3>&1

    Il Bash incorporato execpuò essere usato per applicare un reindirizzamento dello stream alla shell, il che significa che influenza tutti i seguenti comandi. Per ulteriori informazioni, esegui help execnel tuo terminale.

    In questo caso speciale, lo stream 3 viene reindirizzato allo stream 1 (STDOUT), ciò significa che tutto ciò che invieremo allo stream 3 in seguito apparirà nel nostro terminale come se fosse normalmente stampato su STDOUT.

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

    Questa linea è composta da molte parti e strutture sintattiche:

    • result=$(...)
      Questa struttura esegue il comando tra parentesi e assegna l'output (STDOUT) alla variabile bash result. È leggibile fino in fondo $result. Tutto questo è descritto in qualche modo nel prossimo film man bash.

    • dialog --inputbox TEXT HEIGHT WIDTH
      Questo comando mostra una casella TUI con il dato TESTO, un campo di inserimento testo e due pulsanti OK e CANCEL. Se viene selezionato OK, il comando esce con lo stato 0 e stampa il testo immesso su STDERR, se CANCEL viene selezionato, uscirà con il codice 1 e non stampa nulla. Per maggiori informazioni, leggi man dialog.

    • 2>&1 1>&3
      Questi sono due comandi di reindirizzamento. Saranno interpretati da destra a sinistra:

      1>&3 reindirizza il flusso 1 del comando (STDOUT) al flusso personalizzato 3.

      2>&1 reindirizza successivamente lo stream 2 del comando (STDERR) allo stream 1 (STDOUT).

      Ciò significa che tutto ciò che il comando stampa su STDOUT ora appare nello stream 3, mentre tutto ciò che doveva essere mostrato su STDERR ora viene reindirizzato a STDOUT.

    Quindi l'intera riga visualizza un prompt di testo (su STDOUT, che è stato reindirizzato allo stream 3, che alla fine la shell reindirizza nuovamente a STDOUT - vedere il exec 3>&1comando) e assegna i dati immessi (restituiti tramite STDERR, quindi reindirizzati a STDOUT) alla variabile Bash result.

  • exitcode=$?

    Questo codice recupera il codice di uscita del comando precedentemente eseguito (qui da dialog) attraverso la variabile Bash riservata $?(contiene sempre l'ultimo codice di uscita) e semplicemente lo memorizza nella nostra variabile Bash exitcode. Può essere letto di $exitcodenuovo. Puoi cercare maggiori informazioni su questo in man bash, ma ciò potrebbe richiedere del tempo ...

  • exec 3>&-

    Il Bash incorporato execpuò essere usato per applicare un reindirizzamento dello stream alla shell, il che significa che influenza tutti i seguenti comandi. Per ulteriori informazioni, esegui help execnel tuo terminale.

    In questo caso speciale, lo stream 3 viene reindirizzato a "stream -", il che significa che dovrebbe essere chiuso. D'ora in poi i dati inviati allo stream 3 non verranno più reindirizzati da nessuna parte.

  • echo $result $exitcode

    Questo semplice echocomando (ulteriori informazioni su man echo) stampa solo il contenuto delle due variabili Bash resulte exitcodesu STDOUT. Dato che non abbiamo più reindirizzamenti di stream espliciti o impliciti, appariranno davvero su STDOUT e quindi verranno semplicemente visualizzati nel terminale. Che miracolo! ;-)

Sommario:

Innanzitutto, impostiamo la shell per reindirizzare tutto ciò che inviamo allo stream personalizzato 3 su STDOUT, in modo che venga visualizzato nel nostro terminale.

Quindi eseguiamo il dialogcomando, reindirizziamo il suo STDOUT originale al nostro flusso personalizzato 3, perché alla fine deve essere visualizzato, ma dobbiamo temporaneamente usare il flusso STDOUT per qualcos'altro.
Reindirizziamo lo STDERR originale del comando, in cui viene restituito l'input dell'utente della finestra di dialogo, in seguito su STDOUT.
Ora possiamo acquisire STDOUT (che contiene i dati reindirizzati da STDERR) e archiviarli nella nostra variabile $result. Contiene ora l'input dell'utente desiderato!

Vogliamo anche il dialogcodice di uscita del comando, che indica se è stato fatto clic su OK o CANCEL. Questo valore è presentato nella variabile riservata di Bash $?e lo copiamo semplicemente nella nostra variabile $exitcode.

Successivamente chiudiamo di nuovo lo stream 3, poiché non ne abbiamo più bisogno, per interrompere ulteriori reindirizzamenti di esso.

Infine, normalmente trasmettiamo il contenuto di entrambe le variabili $result(l'input dell'utente della finestra di dialogo) e $exitcode(0 per OK, 1 per CANCEL) al terminale.


Penso che l'utilizzo execsia inutilmente complicato. Perché non solo l' --stdoutopzione per noi dialogo reindirizzare il suo output di 2>&1 >/dev/tty?
jarno,

Si prega di vedere la mia risposta .
jarno,

3
Bella risposta! Tuttavia, credo che tu abbia una nota che non è corretta - dici che "saranno interpretati da destra a sinistra" ma credo che non sia vero. Dal manuale di bash gnu.org/software/bash/manual/html_node/Redirections.html indica che i reindirizzamenti avvengono nel momento in cui si verificano (cioè da sinistra a destra)
ralfthewise,

14

Utilizzando gli strumenti propri della finestra di dialogo: --output-fd flag

Se leggi la pagina man per la finestra di dialogo, c'è un'opzione --output-fdche ti permette di impostare esplicitamente dove va l'output (STDOUT 1, STDERR 2), invece di andare su STDERR per impostazione predefinita.

Qui sotto puoi vedermi eseguire il dialogcomando di esempio , affermando esplicitamente che l'output deve andare al descrittore di file 1, che mi consente di salvarlo in MYVAR.

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

inserisci qui la descrizione dell'immagine

Utilizzando pipe denominate

Un approccio alternativo che ha molte potenzialità nascoste è quello di usare qualcosa noto come pipe .

#!/bin/bash

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

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

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


echo  "This is the output " $OUTPUT
# clean up
rm /tmp/namedPipe1 

inserisci qui la descrizione dell'immagine

Una panoramica più approfondita della risposta di user.dz con approccio alternativo

La risposta originale di user.dz e la spiegazione di ByteCommander forniscono entrambe una buona soluzione e una panoramica di ciò che fa. Tuttavia, credo che un'analisi più approfondita potrebbe essere utile per spiegare perché funziona.

Prima di tutto, è importante capire due cose: qual è il problema che stiamo cercando di risolvere e quali sono i meccanismi alla base dei meccanismi di shell con cui abbiamo a che fare. Il compito è acquisire l'output di un comando tramite la sostituzione del comando. Sotto una visione semplicistica che tutti conoscono, le sostituzioni di comandi catturano il stdoutcomando e lo lasciano riutilizzare da qualcos'altro. In questo caso, la result=$(...)parte dovrebbe salvare l'output di qualunque comando sia designato ...in una variabile chiamata result.

Sotto il cofano, la sostituzione dei comandi è effettivamente implementata come pipe, dove c'è un processo figlio (il comando effettivo che viene eseguito) e un processo di lettura (che salva l'output in variabile). Ciò è evidente con una semplice traccia di chiamate di sistema. Si noti che il descrittore di file 3 è la fine della lettura della pipe, mentre 4 è la fine della scrittura. Per il processo figlio di echo, che scrive nel suo stdout- il descrittore di file 1, quel descrittore di file è in realtà copia del descrittore di file 4, che è l'estremità di scrittura della pipe. Si noti che qui stderrnon sta giocando un ruolo, semplicemente perché è stdoutsolo una pipe .

$ strace -f -e pipe,dup2,write,read bash -c 'v=$(echo "X")'
...
pipe([3, 4])                            = 0
strace: Process 6200 attached
[pid  6199] read(3,  <unfinished ...>
[pid  6200] dup2(4, 1)                  = 1
[pid  6200] write(1, "X\n", 2 <unfinished ...>
[pid  6199] <... read resumed> "X\n", 128) = 2
[pid  6200] <... write resumed> )       = 2
[pid  6199] read(3, "", 128)            = 0
[pid  6200] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6200, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

Torniamo alla risposta originale per un secondo. Da ora sappiamo che dialogscrive la casella TUI su stdout, rispondere stderre all'interno della sostituzione dei comandi stdoutviene convogliata da qualche altra parte, abbiamo già parte della soluzione - dobbiamo ricollegare i descrittori di file in modo tale che stderrvengano convogliati al processo di lettura. Questa è la 2>&1parte della risposta. Tuttavia, cosa facciamo con la TUI box?

È qui che entra in gioco il descrittore di file 3. La dup2()syscall ci consente di duplicare i descrittori di file, facendoli effettivamente fare riferimento allo stesso posto, ma possiamo manipolarli separatamente. I descrittori di file di processi a cui è collegato un terminale di controllo in realtà puntano a un dispositivo terminale specifico. Questo è evidente se lo fai

$ ls -l /proc/self/fd
total 0
lrwx------ 1 user1 user1 64 Aug 20 10:30 0 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 1 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 2 -> /dev/pts/5
lr-x------ 1 user1 user1 64 Aug 20 10:30 3 -> /proc/6424/fd

dov'è il /dev/pts/5mio attuale dispositivo pseudo-terminale. Pertanto, se possiamo in qualche modo salvare questa destinazione, possiamo ancora scrivere la casella TUI sullo schermo del terminale. Questo è quello che exec 3>&1fa. Quando si chiama un comando con il reindirizzamento, command > /dev/nullad esempio, la shell passa il suo descrittore di file stdout e quindi usa dup2()per scrivere quel descrittore di file /dev/null. Il execcomando esegue qualcosa di simile aidup2() descrittori di file per l'intera sessione della shell, facendo in modo che qualsiasi comando erediti il ​​descrittore di file già reindirizzato. Lo stesso con exec 3>&1. Il descrittore di file 3ora farà riferimento a / punta al terminale di controllo e qualsiasi comando eseguito in quella sessione della shell lo saprà.

Quindi, quando si result=$(dialog --inputbox test 0 0 2>&1 1>&3);verifica, la shell crea una pipe per la finestra di dialogo da scrivere, ma 2>&1prima farà anche duplicare il descrittore di file del comando 2 sul descrittore di file di scrittura di quella pipe (facendo in modo che l'output vada a leggere la fine della pipe e nella variabile) , mentre il descrittore di file 1 verrà duplicato su 3. In questo modo il descrittore di file 1 farà ancora riferimento al terminale di controllo e la finestra di dialogo TUI apparirà sullo schermo.

Ora, in realtà c'è una scorciatoia per l'attuale terminale di controllo del processo, che è /dev/tty. Pertanto, la soluzione può essere semplificata senza l'uso di descrittori di file, semplicemente in:

result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"

Cose chiave da ricordare:

  • i descrittori di file sono ereditati dalla shell da ciascun comando
  • la sostituzione del comando è implementata come pipe
  • descrittori di file duplicati faranno riferimento allo stesso posto di quello originale, ma possiamo manipolare ciascun descrittore di file separatamente

Guarda anche


La manpage dice anche che l' --stdoutopzione può essere pericolosa e fallisce facilmente su alcuni sistemi, e penso che --output-fd 1stia facendo lo stesso: --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.- Tuttavia, l'idea di pipe denominata è fantastica!
Byte Commander

@ByteCommander "Può fallire" non è molto convincente, in quanto non fornisce esempi. Inoltre, non menzionano nulla --output-fd, che è l'opzione che ho usato qui, non --stdout. In secondo luogo, la finestra di dialogo viene prima disegnata su stdout, l'output restituito è il secondo. Non facciamo queste due cose contemporaneamente. Tuttavia, --output-fd non è necessario uno specifico per utilizzare fd 1 (STDOUT). Può essere facilmente reindirizzato a un altro descrittore di file
Sergiy Kolodyazhnyy,

Non sono sicuro, forse funziona ovunque, forse funziona solo sulla maggior parte dei sistemi. Funziona sul mio e la manpage dice che usare un'opzione simile con cautela è tutto ciò che so per certo. Ma come ho già detto, il +1 è comunque meritato per le pipe nominate.
Byte Commander

Dovrei commentare qui, per mantenere un po 'di equilibrio. Per me, questa potrebbe essere l'unica risposta canonica diretta (1) che utilizza solo lo stesso strumento e ha implementato opzioni senza alcun strumento esterno (2) Funziona in Ubuntu e tutto ciò che riguarda AU. : / purtroppo l'OP sembra abbandonare questa domanda.
user.dz

Qual è il vantaggio di utilizzare qui pipe denominate anziché file normali? Non vuoi eliminare la pipa dopo l'uso?
jarno,

7

: DI non posso spiegarlo !!! Se riesci a capire cosa stanno dicendo nel riferimento: Guida avanzata di script Bash: Capitolo 20. Reindirizzamento I / O , scrivi una nuova risposta e ti darò 50rep

Bounty è stato dato, per spiegazioni vedi la risposta di ByteCommander . :) Questa è una parte della storia.

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

Fonte: La finestra di dialogo in bash non sta acquisendo correttamente le variabili
Riferimento: Guida avanzata di script Bash: Capitolo 20. Reindirizzamento I / O


quell'offerta è ancora valida? Penso di poter spiegare cosa hai trovato lì un anno e mezzo fa ... :-)
Byte Commander

@ByteCommander, ma tuttavia, se puoi fornirlo, te lo darò, sarò alle mie parole: D.
user.dz

@ByteCommander, per favore, chiamami dopo averlo pubblicato.
user.dz

1
Finito! askubuntu.com/a/704616/367990 Spero che tu capisca tutto e ti piaccia l'Eureka! momento. MrGreen Lascia un commento se qualcosa non fosse chiaro.
Byte Commander

4

Questo funziona per me:

#!/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

La pagina di manuale di dialog--stdout:

Uscita diretta all'output standard. Questa opzione è fornita per la compatibilità con Xdialog, tuttavia non è consigliabile utilizzarla negli script portatili, poiché le maledizioni normalmente scrivono i suoi aggiornamenti dello schermo nell'output standard. Se si utilizza questa opzione, la finestra di dialogo tenta di riaprire il terminale in modo che possa scrivere sul display. A seconda della piattaforma e dell'ambiente, ciò potrebbe non riuscire.

Qualcuno può dire in quale piattaforma o ambiente non funziona? Il reindirizzamento dialogdell'output 2>&1 >/dev/ttyinvece funziona meglio?


4

Nel caso in cui qualcun altro sia arrivato qui da Google, e sebbene questa domanda richieda specificamente bash, ecco un'altra alternativa:

Puoi usare zenity . Zenity è un'utilità grafica che può essere utilizzata all'interno di script bash. Ma ovviamente ciò richiederebbe un server X come user877329 ha giustamente sottolineato.

sudo apt-get install zenity

Quindi nella tua sceneggiatura:

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

Link utile .


3
A meno che non ci sia un server X
user877329

1
OP vuole sapere dialog. È come se venissi a chiederti "Come scrivo questo e quello in pitone?", Ma tu mi dai una bash - sono molto felice che si possa fare diversamente, ma non è quello che chiedo
Sergiy Kolodyazhnyy

@Serg il tuo commento non è valido, la mia risposta non lo è: l'utilità fornisce un'alternativa perfettamente valida e semplice alla soluzione richiesta dall'OP.
Wtower,

3

La risposta fornita da Sneetsher è in qualche modo più elegante, ma posso spiegare cosa c'è che non va: il valore di $$è diverso all'interno dei backtick (perché avvia una nuova shell, e$$ è il PID della shell corrente). Ti consigliamo di inserire il nome del file in una variabile, quindi fai riferimento a quella variabile in tutto.

#!/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

In questo caso, evitare il file temporaneo sarebbe una soluzione migliore, ma ci saranno molte situazioni in cui non è possibile evitare un file temporaneo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.