Newsgroup: it.comp.os.linux.iniziare
Titolo: Bash - stdout e stderr su 2 variabili

Curiositá.
Stavo pastrocciando con zenity per avere una interfaccia grafica al comando
zbarimg (decodificatore di qr-code).
Mi serviva avere 2 finestre diverse, a seconda se il comando era eseguito su
una immagine valida oppure una finestra con l'errore di decodifica.
Per ora ho risolto leggendo lo stato di errore in uscita e richiamando una
seconda volta zbarimg reindirizzando stderr su stdout.
QR=$(zbarimg -q $filescelto)
e se $? da un valore diverso da zero:
error=$(zbarimg -v $filescelto 2>&1)
Pensavo di riuscire ad ottenere il tutto con un solo passaggio (utile anche per
eventuali altri casi).
Googlando non ne sono venuto a capo.
-- 
+---------------------------------------------------------------------------+
|  Sappi che la mia spocchiosita' depressiva con pulsione                   |
|  all'autodemolizione e' allo studio di menti brillanti del mondo della    |
|  psichiatria.  Hanno promesso di spiegarmi che cosa ho detto.             |
+-----#17--------------------Campagna contro le pubblicita` idiote.---------+

 eventuali altri casi).
  Il modo più semplice che mi viene in mente è usando una named pipe.
Lo faccio vedere con un esempio creato ad arte.
  Creo le condizioni per avere un comando (ls) che mi crei un output
verso lo standard output e uno verso lo standard error:
$ touch pippo
$ ls pippo pluto
ls: impossibile accedere a pluto: File o directory non esistente
pippo
  Creo la named pipe:
$ mkfifo stderr
  Catturo lo standard output nella variabile STDIN e lo standard error
nella variabile STDERR con un'unica esecuzione di ls:
$ STDIN=$(ls pippo pluto 2> stderr) & STDERR=$(cat stderr)
[1] 3706
$
[1]+  Exit 2                  STDIN=$(ls pippo pluto 2> stderr)
$ echo -e $STDERR\\n$STDIN
ls: impossibile accedere a pluto: File o directory non esistente
pippo
  A fare le cose da manuale si dovrebbe fare un wait sul PID del comando
in background, ossia in questo esempio del comando STDIN=$(ls pippo
pluto 2> stderr), PID che è disponibile nella variabile della shell !.
Questo per essere sicuri che quando si va a leggere il contenuto della
variabile STDIN il comando che le deve fornire i dati abbia terminato
l'esecuzione.  Ossia, prima dell'ultimo comando si dovrebbe fare un:
$ wait $!
 Inutile in questo caso perché si è già visto il messaggio della shell
"[1]+  Exit 2 STDIN=$(ls pippo pluto 2> stderr)" che attesta la fine del
job.  Messaggio che però compare sul terminale di una shell interattiva,
non è prodotto invece da una shell non interattiva (quella di uno script).
  Nota che non ci si prende cura dell'exit status: si hanno le variabili
STDIN e STDERR popolate dai messaggi dello stdout e dello srderr a
prescindere che il comando sia incorso in errori oppure no.
  Ciao,
-- 
Alessandro Selli 
http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.

 prescindere che il comando sia incorso in errori oppure no.
Direi che nell'esempio, l'exit status viene influenzato dall'ultima parte del
comando (cat stderr) per cui é inutilizzabile.
-- 
+---------------------------------------------------------------------------+
|     Il Pascal e' un linguaggio giocattolo fatto per insegnare la          |
|     programmazione ai matematici.                                         |
+-----#20--------------------Campagna contro le pubblicita` idiote.---------+

 comando (cat stderr) per cui ï¿œ inutilizzabile.
  Ti servisse l'exit status di ls (o di quale comando in realtï¿œ usi)
allora devi eseguire wait $!.  Anche se non c'ï¿œ piï¿œ nulla da attendere
(il comando in background ha terminato l'esecuzione prima che sia
lanciato il comando wait), wait $! ti restituisce l'exit code
dell'ultimo comando eseguito in background.


 output verso lo standard out.
per non sbagliare ho batto uno script!
#!/bin/bash
clear
mkfifo stderr
touch pippo
echo
STDIN=$(ls pippo pluto 2> stderr) & STDERR=$(cat stderr)
status=$?
plink=$!
wait $!
echo pid=$plink  ----   status=$status
echo ===[$STDERR]
echo ===[$STDIN]
echo
rm stderr
Che lanciato in /tmp
pid=21170 ---- status=0
===[ls: impossibile accedere a pluto: No such file or directory]
===[]
Sostituendo 
STDIN=$(ls pippo pluto 2> stderr) & STDERR=$(cat stderr)
con
STDIN=$(ls pippo pluto 1> stderr) & STDERR=$(cat stderr)
ottengo:
ls: impossibile accedere a pluto: No such file or directory
pid=21188 ---- status=0
===[pippo]
===[]
Potesse essere (sono su debian)?
big@debian:/tmp$ bash --version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
-- 
+---------------------------------------------------------------------------+
| Da un errore di windows (visto realmente):                                |
| Keyboard not found! Press F1 to continue.                                 |
+-----#28--------------------Campagna contro le pubblicita` idiote.---------+

 Copyright (C) 2007 Free Software Foundation, Inc.
  No, ho sbagliato io.  L'esempio che ho copincollato sembrava
funzionare perché la variabile STDIN (che, tra parentesi, avrei dovuto
chiamare STDOUT) era stata inizializzata al valore "pippo" da una
precedente prova.  In realtà quello che succede nel codice che segue:
STDOUT=$(ls pippo pluto 2> stderr) & STDERR=$(cat stderr)
è che il comando di attribuzione di valore a variabile STDOUT=$(ls pippo
pluto 2> stderr) & , eseguito in background, è eseguito in una
sottoshell mandata automaticamente in esecuzione dalla shell in cui è
eseguito lo script (o la shell del terminale, se il comando è eseguito
in modalità interattiva).  Essendo le variabili confinate ad esistere
solamente nella shell in cui sono generate o, nel caso delle variabili
esportate, in quelle figlie ma mai in quelle genitrici, il valore di
STDOUT è perso.
  Non mi viene in mente soluzione senza usare un file temporaneo al
posto della named pipe, quindi come rimedio ho da proporre solo queste
modifiche al tuo script adesso:
#!/bin/bash
clear
touch pippo
echo
STDOUT=$(ls pippo pluto 2> stderr)
status=$?
STDERR=$(< stderr)
echo status=$status
echo ===[$STDERR]
echo ===[$STDOUT]
echo
rm stderr


 modifiche al tuo script adesso:
Che risolve il problema, ed inoltre permette di avere il result status utile
per sapere come comportarsi.
Ottimo!
Grazie!!!!
-- 
+---------------------------------------------------------------------------+
| Avevo un lavoro, un posto fisso che mi rendeva bene e ci mantenevo tutta  |
| la famiglia. Poi un giorno e' arrivato un tale, che mi ha detto:          |
| Lazzaro, alzati e cammina! Da quel momento ho perso il posto, il lavoro e |
| sono finito in miseria.                                                   |
+-----#30--------------------Campagna contro le pubblicita` idiote.---------+

 rm stderr
Meglio a questo punto mettere all'inizio:
TMPFILE=$(mktemp)
ed usare al posto di stderr, $TMPFILE
-- 
+---------------------------------------------------------------------------+
|  LA FIGA E' IL POSTO PIU' PULITO DEL MONDO PERCHE' CI SCOPANO TUTTI..     |
|  MA ANCHE IL POSTO PIU' SERIO DEL MONDO                                   |
+-----#36--------------------Campagna contro le pubblicita` idiote.---------+