Wie kann ich testen, ob ein Befehl eine leere Zeichenfolge ausgibt?
ifne
. joeyh.name/code/moreutils
Wie kann ich testen, ob ein Befehl eine leere Zeichenfolge ausgibt?
ifne
. joeyh.name/code/moreutils
Antworten:
Zuvor wurde gefragt, wie überprüft werden soll, ob sich Dateien in einem Verzeichnis befinden. Der folgende Code erreicht dies, aber siehe Antwort von rsp für eine bessere Lösung.
Befehle nicht zurückgeben Werte - sie geben sie. Sie können diese Ausgabe mithilfe der Befehlssubstitution erfassen . zB $(ls -A)
. Sie können in Bash wie folgt auf eine nicht leere Zeichenfolge testen:
if [[ $(ls -A) ]]; then
echo "there are files"
else
echo "no files found"
fi
Beachten Sie, dass ich -A
eher als verwendet habe -a
, da die symbolischen Verzeichniseinträge current ( .
) und parent ( ..
) weggelassen werden.
Hinweis: Wie in den Kommentaren erwähnt, werden beim Ersetzen von Befehlen keine nachgestellten Zeilenumbrüche erfasst . Wenn der Befehl nur Zeilenumbrüche ausgibt , erfasst die Ersetzung daher nichts und der Test gibt false zurück. Dies ist im obigen Beispiel zwar sehr unwahrscheinlich, aber möglich, da ein einzelner Zeilenumbruch ein gültiger Dateiname ist! Weitere Informationen in dieser Antwort .
Wenn Sie überprüfen möchten, ob der Befehl erfolgreich ausgeführt wurde, können Sie überprüfen $?
, welcher den Exit-Code des letzten Befehls enthält (Null für Erfolg, Nicht-Null für Fehler). Beispielsweise:
files=$(ls -A)
if [[ $? != 0 ]]; then
echo "Command failed."
elif [[ $files ]]; then
echo "Files found."
else
echo "No files found."
fi
Mehr Infos hier .
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi
Vielen Dank an netj
für einen Vorschlag zur Verbesserung meines Originals:if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi
Dies ist eine alte Frage, aber ich sehe mindestens zwei Dinge, die verbessert oder zumindest geklärt werden müssen.
Das erste Problem, das ich sehe, ist, dass die meisten der hier bereitgestellten Beispiele einfach nicht funktionieren . Sie verwenden die Befehle ls -al
und ls -Al
- beide geben nicht leere Zeichenfolgen in leeren Verzeichnissen aus. In diesen Beispielen wird immer angegeben , dass Dateien vorhanden sind, auch wenn keine vorhanden sind .
Aus diesem Grund sollten Sie nur ls -A
- Warum sollte jemand den -l
Schalter verwenden wollen, was "ein langes Listenformat verwenden" bedeutet, wenn Sie nur testen möchten, ob eine Ausgabe vorhanden ist oder nicht?
Die meisten Antworten hier sind also einfach falsch.
Das zweite Problem ist , dass , während einige Antworten gut funktionieren (diejenigen , die nicht verwendet werden ls -al
oder ls -Al
aber ls -A
stattdessen) sie alle etwas tun:
Was ich stattdessen vorschlagen würde, wäre:
using head -c1
Also zum Beispiel anstelle von:
if [[ $(ls -A) ]]
Ich würde ... benutzen:
if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]
Anstatt:
if [ -z "$(ls -lA)" ]
Ich würde ... benutzen:
if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]
und so weiter.
Bei kleinen Ausgängen ist dies möglicherweise kein Problem, bei größeren Ausgängen kann der Unterschied jedoch erheblich sein:
$ time [ -z "$(seq 1 10000000)" ]
real 0m2.703s
user 0m2.485s
sys 0m0.347s
Vergleichen Sie es mit:
$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]
real 0m0.128s
user 0m0.081s
sys 0m0.105s
Und noch besser:
$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]
real 0m0.004s
user 0m0.000s
sys 0m0.007s
Aktualisiertes Beispiel aus der Antwort von Will Vousden:
if [[ $(ls -A | wc -c) -ne 0 ]]; then
echo "there are files"
else
echo "no files found"
fi
Nach Vorschlägen von netj erneut aktualisiert :
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
echo "there are files"
else
echo "no files found"
fi
Zusätzliches Update von jakeonfire :
grep
wird mit einem Fehler beendet, wenn keine Übereinstimmung vorliegt. Wir können dies nutzen, um die Syntax etwas zu vereinfachen:
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
Wenn der zu testende Befehl ein Leerzeichen ausgeben könnte, das Sie als leere Zeichenfolge behandeln möchten, dann anstelle von:
| wc -c
Du könntest benutzen:
| tr -d ' \n\r\t ' | wc -c
oder mit head -c1
:
| tr -d ' \n\r\t ' | head -c1 | wc -c
oder etwas ähnliches.
Verwenden Sie zunächst einen funktionierenden Befehl .
Vermeiden Sie zweitens unnötiges Speichern im RAM und die Verarbeitung potenziell großer Datenmengen.
In der Antwort wurde nicht angegeben, dass die Ausgabe immer klein ist, daher muss auch die Möglichkeit einer großen Ausgabe berücksichtigt werden.
[[ $(... | head -c | wc -c) -gt 0 ]]
oder [[ -n $(... | head -c1) ]]
sind besser, weil wc -c
die gesamte Ausgabe des Befehls verbrauchen müsste.
if [ -z "$(ls -lA)" ]; then
echo "no files found"
else
echo "There are files"
fi
Dadurch wird der Befehl ausgeführt und geprüft, ob die zurückgegebene Ausgabe (Zeichenfolge) eine Länge von Null hat. Möglicherweise möchten Sie die 'Test'-Handbuchseiten auf andere Flags überprüfen .
Verwenden Sie das "" um das zu prüfende Argument, da sonst leere Ergebnisse zu einem Syntaxfehler führen, da kein zweites zu prüfendes Argument angegeben wird!
Hinweis: Das ls -la
kehrt immer zurück .
und ..
daher funktioniert die Verwendung nicht, siehe ls Handbuchseiten . Obwohl dies bequem und einfach erscheinen mag, wird es wahrscheinlich leicht brechen. Das Schreiben eines kleinen Skripts / einer kleinen Anwendung, die je nach Ergebnis 0 oder 1 zurückgibt, ist viel zuverlässiger!
$(
anstelle von Backquote zu verwenden, weil $(
Nest besser
Für diejenigen, die eine elegante, von der Bash-Version unabhängige Lösung suchen (die eigentlich in anderen modernen Shells funktionieren sollte) und für diejenigen, die gerne Einzeiler für schnelle Aufgaben verwenden. Auf geht's!
ls | grep . && echo 'files found' || echo 'files not found'
(Beachten Sie als einer der erwähnten Kommentare, ls -al
und in der Tat, nur -l
und -a
werden alle etwas zurückgeben, so verwende ich in meiner Antwort einfachls
grep -q ^
stattdessen verwenden, werden auch Zeilenumbrüche abgeglichen, und grep druckt nichts in die Standardausgabe. Darüber hinaus wird es beendet, sobald eine Eingabe empfangen wird, anstatt auf das Ende seines Eingabestreams zu warten.
ls -A
wenn Sie
-z string
True if the length of string is zero.
-n string
string
True if the length of string is non-zero.
Sie können die Kurzversion verwenden:
if [[ $(ls -A) ]]; then
echo "there are files"
else
echo "no files found"
fi
string
ein Synonym für ist -n string
.
Wie Jon Lin kommentierte, ls -al
wird immer (für .
und ..
) ausgegeben . Sie möchten ls -Al
diese beiden Verzeichnisse vermeiden.
Sie können beispielsweise die Ausgabe des Befehls in eine Shell-Variable einfügen:
v=$(ls -Al)
Eine ältere, nicht verschachtelbare Notation ist
v=`ls -Al`
aber ich bevorzuge die nestbare Notation $(
...)
Sie können testen, ob diese Variable nicht leer ist
if [ -n "$v" ]; then
echo there are files
else
echo no files
fi
Und Sie könnten beide als kombinieren if [ -n "$(ls -Al)" ]; then
Ich vermute, Sie möchten die Ausgabe des ls -al
Befehls, also hätten Sie in bash so etwas wie:
LS=`ls -la`
if [ -n "$LS" ]; then
echo "there are files"
else
echo "no files found"
fi
ls
wird nie ausgeführt. Sie prüfen nur, ob die Erweiterung der Variablen LS
nicht leer ist.
-a
Switch gibt niemals keinen Inhalt zurück (dh er gibt Inhalt zurück, unabhängig davon, ob Dateien vorhanden sind oder nicht), da immer das Implizite .
und die ..
Verzeichnisse vorhanden sind.
Hier ist eine Lösung für extremere Fälle:
if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi
Das wird funktionieren
jedoch,
Alle bisher gegebenen Antworten beziehen sich auf Befehle, die eine nicht leere Zeichenfolge beenden und ausgeben.
Die meisten sind in folgenden Sinnen gebrochen:
yes
. B. ).Um all diese Probleme zu beheben und die folgende Frage effizient zu beantworten,
Wie kann ich testen, ob ein Befehl eine leere Zeichenfolge ausgibt?
Sie können verwenden:
if read -n1 -d '' < <(command_here); then
echo "Command outputs something"
else
echo "Command doesn't output anything"
fi
Sie können auch eine Zeitüberschreitung hinzufügen, um zu testen, ob ein Befehl innerhalb einer bestimmten Zeit eine nicht leere Zeichenfolge ausgibt, indem Sie read
die -t
Option 'verwenden. ZB für eine Zeitüberschreitung von 2,5 Sekunden:
if read -t2.5 -n1 -d '' < <(command_here); then
echo "Command outputs something"
else
echo "Command doesn't output anything"
fi
Anmerkung. Wenn Sie der Meinung sind, dass Sie feststellen müssen, ob ein Befehl eine nicht leere Zeichenfolge ausgibt, liegt höchstwahrscheinlich ein XY-Problem vor.
seq 1 10000000
, seq 1 20
, und printf""
). Das einzige Matchup, das dieser Leseansatz nicht gewann, war der -z "$(command)"
Ansatz für eine leere Eingabe. Selbst dann verliert es nur um etwa 10-15%. Sie scheinen ausgeglichen zu sein seq 1 10
. Unabhängig davon wird der -z "$(command)"
Ansatz mit zunehmender Eingabegröße so langsam, dass ich ihn nicht für Eingaben mit variabler Größe verwenden würde.
Hier ist ein alternativer Ansatz, bei dem std-out und std-err eines Befehls in eine temporäre Datei geschrieben und anschließend überprüft wird, ob diese Datei leer ist. Ein Vorteil dieses Ansatzes besteht darin, dass beide Ausgänge erfasst werden und keine Unterschalen oder Rohre verwendet werden. Diese letzteren Aspekte sind wichtig, da sie die Behandlung des Bash-Exit-Trapping beeinträchtigen können (z. B. hier ).
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"
Manchmal möchten Sie die Ausgabe speichern, wenn sie nicht leer ist, um sie an einen anderen Befehl zu übergeben. Wenn ja, könnten Sie so etwas verwenden
list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
/bin/rm $list
fi
Auf diese Weise rm
bleibt der Befehl nicht hängen, wenn die Liste leer ist.