Wenn ich prüfen möchte, ob eine einzelne Datei vorhanden ist, kann ich sie mit test -e filename
oder testen [ -e filename ]
.
Angenommen, ich habe einen Glob und möchte wissen, ob Dateien vorhanden sind, deren Namen mit dem Glob übereinstimmen. Der Glob kann mit 0 Dateien übereinstimmen (in diesem Fall muss ich nichts tun) oder mit 1 oder mehreren Dateien (in diesem Fall muss ich etwas tun). Wie kann ich testen, ob ein Glob Übereinstimmungen hat? (Es ist mir egal, wie viele Übereinstimmungen es gibt, und es wäre am besten, wenn ich dies mit einer if
Anweisung und ohne Schleifen tun könnte (einfach, weil ich das am besten lesbar finde).
( test -e glob*
schlägt fehl, wenn der Glob mit mehr als einer Datei übereinstimmt.)
Antworten:
Entkomme dem Muster oder es wird zu Übereinstimmungen vorerweitert.
Der Exit-Status lautet:
stdout
ist eine Liste von Dateien, die dem Glob entsprechen .Ich denke, dies ist die beste Option in Bezug auf Prägnanz und Minimierung möglicher Nebenwirkungen.
UPDATE : Beispiel für eine Verwendung angefordert.
quelle
compgen
ein bash- spezifischer integrierter Befehl ist und nicht Teil der von der POSIX-Standard-Unix-Shell angegebenen integrierten Befehle ist. pubs.opengroup.org/onlinepubs/9699919799 pubs.opengroup.org/onlinepubs/9699919799/utilities/… Vermeiden Sie daher die Verwendung in Skripten, in denen die Portabilität auf andere Shells ein Problem darstellt .if ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fi
- Vielleicht nützlich für Code-Golf? Schlägt fehl, wenn es eine Datei mit dem gleichen Namen wie der Glob gibt, mit der der Glob nicht hätte übereinstimmen sollen, aber wenn dies der Fall ist, haben Sie wahrscheinlich größere Probleme.if ls /tmp/*Files &> /dev/null; then echo exists; fi
compgen
, sieheman bash
oder mithelp compgen
Die Nullglob-Shell-Option ist in der Tat ein Bashismus.
Um zu vermeiden, dass der Nullglob-Status mühsam gespeichert und wiederhergestellt werden muss, habe ich ihn nur in der Subshell festgelegt, die den Glob erweitert:
Verwenden Sie find für eine bessere Portabilität und flexibleres Globbing:
Explizite -print-quit- Aktionen werden anstelle der impliziten Standard- print- Standardaktion für die Suche verwendet, sodass die Suche beendet wird, sobald die erste Datei gefunden wird, die den Suchkriterien entspricht. Wenn viele Dateien übereinstimmen, sollte dies viel schneller als oder ausgeführt werden, und es wird auch die Möglichkeit vermieden, die erweiterte Befehlszeile zu überfüllen (einige Shells haben eine Längenbeschränkung von 4 KB).
echo glob*
ls glob*
Wenn sich find wie übertrieben anfühlt und die Anzahl der Dateien, die wahrscheinlich übereinstimmen, gering ist, verwenden Sie stat:
quelle
find
scheint genau richtig zu sein. Es gibt keine Eckfälle, da die Shell keine Erweiterung durchführt (und einen nicht erweiterten Glob an einen anderen Befehl übergibt), zwischen Shells portierbar ist (obwohl anscheinend nicht alle von Ihnen verwendeten Optionen von POSIX angegeben werden) und schneller alsls -d glob*
(die zuvor akzeptierte Antwort), weil sie stoppt, wenn sie das erste Spiel erreicht.shopt -u failglob
da diese Optionen in irgendeiner Weise in Konflikt zu geraten scheinen.find
Lösung stimmt auch mit einem Dateinamen ohne Glob-Zeichen überein. In diesem Fall wollte ich das. Nur etwas zu beachten.-maxdepth
Option für einen POSIX-Fund ersetzt wird.quelle
nullglob
anstatt zu prüfen, ob ein einzelnes Ergebnis dem Muster selbst entspricht. Einige Muster können mit Namen übereinstimmen, die genau dem Muster selbst entsprechen (za*b
. B. aber nicht zBa?b
oder[a]
).touch '*py'
), aber das weist mich in eine andere gute Richtung."$M"
als Abkürzung für verwenden"${M[0]}"
. Andernfalls haben Sie die Glob-Erweiterung bereits in einer Array-Variablen, sodass Sie sie als Liste an andere Dinge übergeben müssen, anstatt sie dazu zu bringen, den Glob erneut zu erweitern.[
Prozess zu erzeugen ) mitif [[ $M ]]; then ...
ich mag
Dies ist sowohl lesbar als auch effizient (es sei denn, es gibt eine große Anzahl von Dateien).
Der Hauptnachteil ist, dass es viel subtiler ist als es aussieht, und ich fühle mich manchmal gezwungen, einen langen Kommentar hinzuzufügen.
Wenn es eine Übereinstimmung gibt,
"glob*"
wird diese von der Shell erweitert und alle Übereinstimmungen werden an übergebenexists()
, wodurch die erste überprüft und der Rest ignoriert wird.Wenn es keine Übereinstimmung gibt,
"glob*"
wird an übergebenexists()
und festgestellt, dass es dort auch nicht existiert.Bearbeiten: Möglicherweise liegt ein falsches Positiv vor, siehe Kommentar
quelle
*.[cC]
(es gibt möglicherweise keinec
oder eineC
Datei, aber eine aufgerufene Datei*.[cC]
), oder ein falsches Negativ, wenn die erste daraus erweiterte Datei beispielsweise ein Symlink zu einer nicht vorhandenen Datei oder zu einer Datei in a ist Verzeichnis, auf das Sie keinen Zugriff haben (Sie möchten übrigens ein hinzufügen|| [ -L "$1" ]
).-e
, wenn 0 oder 1 Übereinstimmungen vorliegen . Es funktioniert nicht für mehrere Übereinstimmungen, da dies zu einem[ -e file1 file2 ]
Fehlschlag führen würde. Unter github.com/koalaman/shellcheck/wiki/SC2144 finden Sie auch die Gründe und Lösungsvorschläge.Wenn Sie Globfail eingestellt haben, können Sie dieses verrückte verwenden (was Sie wirklich nicht sollten)
oder
quelle
(shopt -s failglob; : *) 2>/dev/null && echo exists
test -e hat die unglückliche Einschränkung, dass defekte symbolische Links nicht existieren. Vielleicht möchten Sie auch nach diesen suchen.
quelle
-o
und-a
intest
/[
. Hier schlägt dies$1
beispielsweise=
bei den meisten Implementierungen fehl . Verwenden Sie[ -e "$1" ] || [ -L "$1" ]
stattdessen.Um die Antwort des MYYN etwas zu vereinfachen, basierend auf seiner Idee:
quelle
[a]
, eine Datei mit dem Namen haben[a]
, aber keine Datei mit dem Namena
? Ich magnullglob
das immer noch. Einige mögen dies als pedantisch ansehen, aber wir könnten genauso gut richtig sein, wie es vernünftig ist.[a]
sollte nur übereinstimmena
, nicht der wörtliche Dateiname[a]
.Basierend auf der Antwort von flabdablet sieht es für mich so aus, als ob es am einfachsten (nicht unbedingt am schnellsten) ist, sich selbst zu finden , während die Glob-Erweiterung auf der Shell verbleibt, wie:
Oder
if
wie folgt:quelle
find
Antwort von flabdablet, da es Pfade im Glob akzeptiert und knapper ist (nicht erforderlich-maxdepth
usw.). Es scheint auch besser zu sein als seinestat
Antwort, da es nicht weiterhin das Extrastat
für jedes weitere Glob-Match leistet. Ich würde mich freuen, wenn jemand Eckfälle beitragen könnte, in denen dies nicht funktioniert.-maxdepth 0
weil es mehr Flexibilität beim Hinzufügen von Bedingungen ermöglicht. Angenommen, ich möchte das Ergebnis nur auf übereinstimmende Dateien beschränken. Ich könnte es versuchenfind $glob -type f -quit
, aber das würde true zurückgeben, wenn der Glob NICHT mit einer Datei übereinstimmt, sondern mit einem Verzeichnis, das eine Datei enthält (sogar rekursiv). Im Gegensatz dazufind $glob -maxdepth 0 -type f -quit
würde true nur zurückgegeben, wenn der Glob selbst mindestens einer Datei entspricht. Beachten Sie, dassmaxdepth
dies nicht verhindert, dass der Glob eine Verzeichniskomponente enthält. (FYI2>
ist ausreichend. Keine Notwendigkeit für&>
)find
besteht darin, zu vermeiden, dass die Shell eine potenziell große Liste von Glob-Übereinstimmungen generiert und sortiert.find -name ... -quit
entspricht höchstens einem Dateinamen. Wenn ein Skript darauf angewiesen ist, eine von der Shell generierte Liste von Glob-Übereinstimmungen an zu übergebenfind
, wird durch das Aufrufenfind
nur ein unnötiger Prozessstartaufwand verursacht. Das einfache Testen der resultierenden Liste direkt auf Nichtleere ist schneller und klarer.Ich habe noch eine andere Lösung:
Das funktioniert gut für mich. Gibt es einige Eckfälle, die ich vermisse?
quelle
In Bash können Sie zu einem Array globalisieren. Wenn der Glob nicht übereinstimmt, enthält Ihr Array einen einzelnen Eintrag, der keiner vorhandenen Datei entspricht:
Hinweis: Wenn Sie festgelegt haben
nullglob
, handeltscripts
es sich um ein leeres Array, und Sie sollten stattdessen mit[ "${scripts[*]}" ]
oder mit testen[ "${#scripts[*]}" != 0 ]
. Wenn Sie eine Bibliothek schreiben, die mit oder ohne funktionieren mussnullglob
, möchten SieEin Vorteil dieses Ansatzes besteht darin, dass Sie dann die Liste der Dateien haben, mit denen Sie arbeiten möchten, anstatt den Glob-Vorgang wiederholen zu müssen.
quelle
if [ -e "${scripts[0]}" ]...
? Berücksichtigen Sie auch die Möglichkeit, die Nomenmenge der Shell-Option festzulegen ?nounset
es aktiv ist. Außerdem kann es (etwas) billiger sein, zu testen, ob die Zeichenfolge nicht leer ist, als zu überprüfen, ob eine Datei vorhanden ist. Angesichts der Tatsache, dass wir gerade einen Glob ausgeführt haben, ist dies jedoch unwahrscheinlich. Dies bedeutet, dass der Verzeichnisinhalt im Cache des Betriebssystems aktuell sein sollte.Dieser Gräuel scheint zu funktionieren:
Es erfordert wahrscheinlich Bash, nicht sh.
Dies funktioniert, weil die Option nullglob bewirkt, dass der Glob als leere Zeichenfolge ausgewertet wird, wenn keine Übereinstimmungen vorhanden sind. Daher zeigt jede nicht leere Ausgabe des Echo-Befehls an, dass der Glob mit etwas übereinstimmt.
quelle
if [ "`echo *py`" != "*py"]
*py
.py
,`echo *py`
wird ausgewertet*py
.*py
, was das falsche Ergebnis ist.*py
passende Datei vorhanden ist, gibt Ihr Skript "Glob Matched" aus?Ich habe diese Antwort nicht gesehen und dachte, ich würde sie dort veröffentlichen:
quelle
Erläuterung
Wenn es keine Übereinstimmung für gibt
glob*
,$1
wird enthalten'glob*'
. Der Test-f "$1"
ist nicht wahr, da dieglob*
Datei nicht vorhanden ist.Warum das besser ist als Alternativen
Dies funktioniert mit sh und Derivaten: ksh und bash. Es wird keine Sub-Shell erstellt.
$(..)
und`...`
Befehle erstellen eine Unter-Shell; Sie geben einen Prozess vor und sind daher langsamer als diese Lösung.quelle
So in Bash(Testdateien enthalten
pattern
):Es ist viel besser als
compgen -G
: weil wir mehr Fälle und genauer unterscheiden können.Kann mit nur einem Platzhalter arbeiten
*
quelle
Beachten Sie, dass dies sehr zeitaufwändig sein kann, wenn viele Übereinstimmungen vorliegen oder der Dateizugriff langsam ist.
quelle
[a]
wird, wenn die Datei[a]
vorhanden ist und die Dateia
fehlt. Es wird "gefunden" angezeigt, obwohl die einzige Datei, mit der es übereinstimmen solltea
, nicht vorhanden ist.quelle
nullglob
Shell-Option verwenden.nullglob
. Das Vergleichen eines einzelnen Ergebnisses mit dem ursprünglichen Muster ist ein Problem (und neigt zu falschen Ergebnissen)nullglob
.nullglob
Kludge genannt.quelle
glob*
und Sie beispielsweise nicht über die Schreibfunktion verfügen, um diese Verzeichnisse aufzulisten.ls | grep -q "glob.*"
Nicht die effizienteste Lösung (wenn sich eine Menge Dateien im Verzeichnis befinden, kann dies langsam sein), aber es ist einfach, leicht zu lesen und hat auch den Vorteil, dass reguläre Ausdrücke leistungsfähiger sind als einfache Bash-Glob-Muster.
quelle
quelle