Reindirizza l'output parziale del processo in background e memorizzalo in variabile
Sfondo:
Sto lavorando a un progetto in cui un server viene avviato su una porta disponibile casuale. Il server viene avviato da un comando:
node server.js
Una volta che il server è pronto e inizia a servire su una porta disponibile, restituisce (insieme a una serie di altri log) l'URL completo del server (incluso il numero di porta) su stdout nella seguente forma:
Server started! Listening on URL: http://localhost:12927
Questo processo del server continua a funzionare finché non lo uccidi manualmente (e continua a stampare altri registri).
Problema reale:
Sto scrivendo uno script per avviare il server e quindi ascoltare il suo output. Non appena ricevo il messaggio qualcosa del genere "Server started! Listening on URL: [SERVER_URL]"
, voglio memorizzarlo SERVER_URL
in una variabile ed elaborarlo più tardi nello script (ad esempio aprire un browser per i test di automazione).
Quello che ho provato:
Avendo solo una conoscenza di base di Bash, ho provato le seguenti opzioni (ovviamente, dopo aver effettuato ricerche su SE):
Ho studiato un po 'sull'uso
grep
con regex. Funziona abbastanza bene se lo faccio manualmente (cioè non memorizzandolo in una variabile e semplicemente collegandolo a unecho
comando.SERVER_URL=$(node server.js | grep -m 1 -ohP 'Server started! Listening on URL: \K([^\s$]*)')
Tuttavia, si blocca su questa linea, probabilmente sta aspettando che il server del nodo venga ucciso. La mia esigenza è chiara: voglio popolare SERVER_URL dall'output del server senza aspettarlo oltre il punto in cui sputa quell'URL su stdout.
Ho anche giocherellato un po 'con il reindirizzamento e la sostituzione del processo . Ho appreso che il pipe attende effettivamente il completamento del processo di origine. Ho provato a fare quanto segue con il reindirizzamento, ma non funziona:
SERVER_URL=$(node server.js >&3 | grep -m 1 -ohP 'Server started! Listening on URL: \K([^\s$]*)')
Sta generando una strana eccezione "Evento" errore "non gestito" (Errore: scrivere EPIPE).
Qualsiasi aiuto è apprezzato. Grazie!
Risposte
Sì, in:
var=$(cmd | grep -m1 pattern)
cmd
Lo stdout di è un pipe per grep
e lo grep
stdout di è un pipe per la shell.
Dopo grep
aver trovato la prima corrispondenza, la visualizza sullo stdout ed esce.
La shell leggerà grep
l'output, vedrà la fine del file quando grep
esce, ma nel caso di bash
, la shell attenderà anche cmd
di uscire.
Ora, poiché grep
è uscito, lo cmd
stdout di ora è un tubo rotto. Quindi la prossima volta che cmd
scrive su stdout, riceverà un segnale SIGPIPE e morirà.
Qui, la domanda è: cosa vuoi fare con il resto cmd
dell'output di una volta che hai letto la prima riga che corrisponde al pattern?
Se non ti interessa puoi eseguire un cat
comando in background che lo legge e lo scarta:
{
url=$(grep -m1 -oP 'Server started! Listening on URL: \K\S+')
cat <&0 > /dev/null & # <&0 to work around the fact that bash redirects
# background commands stdin from /dev/null
} < <(node server.js)
Se vuoi tenerlo, puoi reindirizzarlo a qualche file e usarlo tail -f
per esempio nella shell per aspettare il messaggio URL:
node server.js > server.log &
{
IFS= read -r pid
url=$(grep -m1 -oP 'Server started! Listening on URL: \K\S+') kill -- "$pid"
} < <(echo "$BASHPID"; exec tail -n +1 -f server.log)
Qui passare il pid di coda in modo da ucciderlo dopo che il messaggio è stato trovato piuttosto che aspettare tail
di essere ucciso da un SIGPIPE che potrebbe non accadere tail
mai se non emettere mai altro.