Verifica se un comando genera una stringa vuota


237

Come posso verificare se un comando genera una stringa vuota?


7
Un comando non restituisce una stringa (restituisce un piccolo codice di uscita intero, in genere 0 per esito positivo e 1 o 2 in caso di errore), ma può generare alcuni dati da inserire in una stringa.
Basile Starynkevitch,

@BasileStarynkevitch questa è la cosa di cui ho bisogno. So che il comando restituisce il codice di uscita. Ma i miei comandi il codice di uscita è sempre 0. quindi devo controllare l'output
barp

1
Dovresti leggere alcuni tutorial di scripting di Bash tldp.org/LDP/abs/html
Basile Starynkevitch,

Joey Hess ha un'utilità ifneper questo. joeyh.name/code/moreutils
tripleee,

Risposte:


309

In precedenza, la domanda chiedeva come verificare se ci sono file in una directory. Il seguente codice lo raggiunge, ma vedi la risposta di rsp per una soluzione migliore.


Uscita vuota

I comandi non restituiscono valori, ma li generano. È possibile acquisire questo output utilizzando la sostituzione dei comandi ; es $(ls -A). Puoi testare una stringa non vuota in Bash in questo modo:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Nota che ho usato -Apiuttosto che -a, dal momento che omette la corrente simbolica ( .) e parent (.. voci directory ).

Nota: come sottolineato nei commenti, la sostituzione dei comandi non acquisisce nuove righe finali . Pertanto, se il comando genera solo nuove righe, la sostituzione non catturerà nulla e il test restituirà false. Anche se molto improbabile, questo è possibile nell'esempio sopra, poiché una singola riga è un nome file valido! Maggiori informazioni in questa risposta .


Codice di uscita

Se si desidera verificare che il comando sia stato completato correttamente, è possibile controllare $?, che contiene il codice di uscita dell'ultimo comando (zero per esito positivo, diverso da zero per errore). Per esempio:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

Maggiori informazioni qui .


4
Puoi ometterlo -n , quindi sarà soloif [[ $(ls -A) ]]; then
utente

6
Questo metodo fornisce false per i comandi che producono solo nuove righe.
Ciro Santilli 18 冠状 病 六四 事件 法轮功

89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Grazie a netj per un suggerimento per migliorare il mio originale:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


Questa è una vecchia domanda ma vedo almeno due cose che richiedono qualche miglioramento o almeno qualche chiarimento.

Primo problema

Il primo problema che vedo è che la maggior parte degli esempi forniti qui semplicemente non funzionano . Usano i comandi ls -ale ls -Al- entrambi i quali producono stringhe non vuote in directory vuote. Questi esempi riportano sempre che ci sono file anche quando non ce ne sono .

Per questo motivo dovresti usare solo ls -A- Perché qualcuno dovrebbe usare l' -lopzione che significa "usa un formato di elenco lungo" quando tutto ciò che vuoi è testare se c'è qualche output o no, comunque?

Quindi la maggior parte delle risposte qui sono semplicemente errate.

Secondo problema

Il secondo problema è che mentre alcune risposte funzionano bene (quelle che non lo fanno utilizzano ls -alo ls -Alma ls -Ainvece) tutti lo fanno qualcosa di simile:

  1. eseguire un comando
  2. buffer il suo intero output in RAM
  3. converti l'output in un'enorme stringa a riga singola
  4. confronta quella stringa con una stringa vuota

Quello che suggerirei invece di fare sarebbe:

  1. eseguire un comando
  2. contare i caratteri nel suo output senza memorizzarli
    • o anche meglio - conta il numero massimo di 1 carattere using head -c1
      (grazie a netj per aver pubblicato questa idea nei commenti qui sotto)
  3. confronta quel numero con zero

Ad esempio, invece di:

if [[ $(ls -A) ]]

Io userei:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Invece di:

if [ -z "$(ls -lA)" ]

Io userei:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

e così via.

Per output piccoli potrebbe non essere un problema, ma per output più grandi la differenza può essere significativa:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

Confronta con:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

E ancora meglio:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Esempio completo

Esempio aggiornato dalla risposta di Will Vousden:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Aggiornato di nuovo dopo i suggerimenti di netj :

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Aggiornamento aggiuntivo di jakeonfire :

grepuscirà con un errore se non c'è corrispondenza. Possiamo trarne vantaggio per semplificare leggermente la sintassi:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Eliminare gli spazi bianchi

Se il comando che stai testando potrebbe generare degli spazi bianchi che vuoi trattare come una stringa vuota, invece di:

| wc -c

potresti usare:

| tr -d ' \n\r\t ' | wc -c

o con head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

o qualcosa di simile.

Sommario

  1. Innanzitutto, utilizza un comando che funzioni .

  2. In secondo luogo, evitare l'archiviazione non necessaria nella RAM e l'elaborazione di dati potenzialmente enormi.

La risposta non ha specificato che l'output è sempre piccolo, quindi è necessario considerare anche una possibilità di output di grandi dimensioni.


3
[[ $(... | head -c | wc -c) -gt 0 ]]o [[ -n $(... | head -c1) ]]sono migliori perché wc -cdovrebbero consumare l'intero output del comando.
Netj,

@netj Questa è un'ottima idea. Vedi la mia risposta aggiornata: ho aggiunto il tuo suggerimento. Molte grazie!
rsp,

Qualche idea su come ottenere l'output?
fahrradflucht,

Penso che l'originale (senza testa) sia migliore nel caso generale. Non è sempre una buona idea uccidere il comando non appena inizia a scrivere l'output!
inizio

@stk La maggior parte dei programmi uscirà in modo sicuro dopo aver ricevuto un segnale di interruzione.
cambunctious

40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

Questo eseguirà il comando e verificherà se l'output restituito (stringa) ha lunghezza zero. Potresti voler controllare le pagine del manuale 'test' per altre bandiere.

Usa "" attorno all'argomento che viene controllato, altrimenti i risultati vuoti comporteranno un errore di sintassi poiché non c'è un secondo argomento (da controllare) dato!

Nota: che ls -laritorna sempre .e ..quindi l'utilizzo che non funzionerà, consultare le pagine di manuale . Inoltre, sebbene possa sembrare comodo e facile, suppongo che si rompa facilmente. Scrivere un piccolo script / applicazione che restituisce 0 o 1 a seconda del risultato è molto più affidabile!


Potresti preferire utilizzare $(invece di backquote, perché $(nidifica meglio
Basile Starynkevitch

Hai ragione è meglio usarli sempre, anche se non abbiamo bisogno di nidificare qui.
Veger,

23

Per coloro che desiderano una soluzione elegante, indipendente dalla versione bash (in realtà dovrebbe funzionare in altre shell moderne) e coloro che amano usare una linea per compiti veloci. Eccoci qui!

ls | grep . && echo 'files found' || echo 'files not found'

(nota come uno dei commenti menzionati, ls -ale in effetti solo -le -arestituirà qualcosa, quindi nella mia risposta uso semplicels


5
Se si utilizza grep -q ^invece, verranno abbinati anche i caratteri di nuova riga e grep non stamperà nulla sull'output standard. Inoltre, uscirà non appena riceverà qualsiasi input, anziché attendere la fine del suo flusso di input.
mortehu

Dovresti iniziare con ls -Ase vuoi includere dot-file (o directory)
wrlee il

13

Manuale di riferimento di Bash

6.4 Espressioni condizionali di Bash

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

Puoi usare la versione abbreviata:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

1
Manca un -z o -n tra parentesi [[...]].
71GA

4
@ 71GA: Forse è facile da trascurare, ma se guarderai attentamente, vedrai che stringè solo sinonimo di -n string.
utente

10

Come ha commentato Jon Lin, ls -aluscirà sempre (per .e ..). Tu vuoils -Al evitare queste due directory.

Ad esempio, è possibile inserire l'output del comando in una variabile shell:

v=$(ls -Al)

Una notazione più vecchia, non annidabile, è

v=`ls -Al`

ma preferisco la notazione annidabile $(...)

È possibile verificare se quella variabile non è vuota

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

E potresti combinare entrambi come if [ -n "$(ls -Al)" ]; then


5

Immagino che tu voglia l'output del ls -alcomando, quindi in bash avresti qualcosa del tipo:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

Questo non funziona: il comando lsnon viene mai eseguito. Verifica solo se l' espansione della variabile LS non è vuota.
gniourf_gniourf,

1
@gniourf_gniourf - cosa te lo fa pensare? L'incantesimo di backtick non è certo carino o flessibile come $ (), ma funziona ...
tink

L' -ainterruttore non tornerà mai più alcun contenuto (cioè, tornerà contenuto se vi sono file o no) in quanto c'è sempre la implicita .e ..le directory presenti.
wrlee

3

Ecco una soluzione per casi più estremi:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

Questo funzionerà

  • per tutte le conchiglie Bourne;
  • se l'output del comando è tutti zero;
  • in modo efficiente indipendentemente dalle dimensioni dell'output;

però,

  • il comando oi suoi sottoprocessi verranno eliminati una volta emesso qualcosa.

1

Tutte le risposte fornite finora hanno a che fare con comandi che terminano e generano una stringa non vuota.

La maggior parte sono rotti nei seguenti sensi:

  • Non gestiscono correttamente i comandi che emettono solo nuove righe;
  • a partire da Bash≥4.4 la maggior parte invierà spam standard se il comando genera byte nulli (poiché usano la sostituzione dei comandi);
  • la maggior parte consumerà l'intero flusso di output, quindi attenderà il termine del comando prima di rispondere. Alcuni comandi non terminano mai (provare, ad es yes.).

Quindi, per risolvere tutti questi problemi e rispondere in modo efficiente alla seguente domanda,

Come posso verificare se un comando genera una stringa vuota?

Puoi usare:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

È inoltre possibile aggiungere un timeout in modo da verificare se un comando genera una stringa non vuota entro un determinato tempo, usando readl' -topzione di. Ad esempio, per un timeout di 2,5 secondi:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Osservazione. Se ritieni di dover determinare se un comando genera una stringa non vuota, molto probabilmente hai un problema XY.


Penso che questa risposta sia piuttosto sottovalutata. Ero prestazioni testando alcune soluzioni qui utilizzando 100 iterazioni per ciascuno dei 3 scenari ingresso ( seq 1 10000000, seq 1 20e printf""). L'unico matchup che questo approccio di lettura non ha vinto è stato con l' -z "$(command)"approccio per un input vuoto. Anche allora, perde solo circa il 10-15%. Sembrano andare in pareggio seq 1 10. Indipendentemente da ciò, l' -z "$(command)"approccio diventa così lento con l'aumentare delle dimensioni dell'input che eviterei di usarlo per qualsiasi input di dimensioni variabili.
abathur,

0

Ecco un approccio alternativo che scrive lo std-out e lo std-err di alcuni comandi in un file temporaneo, quindi controlla se quel file è vuoto. Un vantaggio di questo approccio è che cattura entrambi gli output e non usa sub-shell o pipe. Questi ultimi aspetti sono importanti perché possono interferire con la gestione delle uscite di trapping bash (ad es. Qui )

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"

0

A volte si desidera salvare l'output, se non è vuoto, per passarlo a un altro comando. Se è così, potresti usare qualcosa del genere

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

In questo modo, il rmcomando non si bloccherà se l'elenco è vuoto.

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.