Wenn ich das Vorhandensein einer einzelnen Datei überprüfen möchte, kann ich sie mit test -e filename
oder [ -e filename ]
testen.
Angenommen, ich habe einen Glob und möchte wissen, ob Dateien existieren, deren Namen mit dem Glob übereinstimmen. Der Glob kann mit 0 Dateien übereinstimmen (in diesem Fall muss ich nichts tun) oder er kann mit einer oder mehreren Dateien übereinstimmen (in diesem Fall muss ich etwas tun). Wie kann ich testen, ob ein Glob eine Übereinstimmung 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.)
Bash spezifische Lösung:
compgen -G "<glob-pattern>"
Entkomme dem Muster oder es wird in Übereinstimmungen erweitert.
Ausgangsstatus ist:
stdout
ist eine Liste von Dateien, die mit dem Glob übereinstimmen.
Ich denke, dies ist die beste Option in Bezug auf Prägnanz und die Minimierung möglicher Nebenwirkungen.
UPDATE: Beispielnutzung angefordert.
if compgen -G "/tmp/someFiles*" > /dev/null; then
echo "Some files exist."
fi
Die Shell-Option von nullglob ist in der Tat ein Bashmus.
Um das langwierige Speichern und Wiederherstellen des Nullglob-Status zu vermeiden, würde ich ihn nur in die Subshell setzen, die den Glob erweitert:
if test -n "$(shopt -s nullglob; echo glob*)"
then
echo found
else
echo not found
fi
Verwenden Sie find für eine bessere Portabilität und flexibleres Globbing:
if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
echo found
else
echo not found
fi
Explizite -print -quit -Aktionen werden für find anstelle der standardmäßigen impliziten -print -Aktion verwendet, sodass find beendet wird, sobald die erste Datei gefunden wird, die der Suche entspricht Kriterien. Wenn viele Dateien übereinstimmen, sollte dies viel schneller als echo glob*
oder ls glob*
ausgeführt werden, und es wird auch die Möglichkeit vermieden, die erweiterte Befehlszeile zu überfüllen (einige Shells haben ein Längenlimit von 4K).
Wenn sich find als übertrieben anfühlt und die Anzahl der Dateien, die wahrscheinlich übereinstimmen, gering ist, verwenden Sie stat:
if stat -t glob* >/dev/null 2>&1
then
echo found
else
echo not found
fi
#!/usr/bin/env bash
# If it is set, then an unmatched glob is swept away entirely --
# replaced with a set of zero words --
# instead of remaining in place as a single Word.
shopt -s nullglob
M=(*px)
if [ "${#M[*]}" -ge 1 ]; then
echo "${#M[*]} matches."
else
echo "No such files."
fi
Ich mag
exists() {
[ -e "$1" ]
}
if exists glob*; then
echo found
else
echo not found
fi
Dies ist sowohl lesbar als auch effizient (sofern nicht sehr viele Dateien vorhanden sind).
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, wird "glob*"
von der Shell erweitert und alle Übereinstimmungen werden an exists()
übergeben, die die erste prüft und den Rest ignoriert.
Wenn es keine Übereinstimmung gibt, wird "glob*"
an exists()
übergeben und dort nicht vorhanden gefunden.
Edit: es kann ein falsch positives Ergebnis geben, siehe comment
test -e hat den unglücklichen Vorbehalt, dass gebrochene symbolische Links als nicht vorhanden betrachtet werden. Vielleicht möchten Sie auch nach diesen suchen.
function globexists {
test -e "$1" -o -L "$1"
}
if globexists glob*; then
echo found
else
echo not found
fi
Wenn du GlobFail gesetzt hast, kannst du dieses verrückte verwenden (was du eigentlich nicht solltest)
shopt -s failglob # exit if * does not match
( : * ) && echo 0 || echo 1
oder
q=( * ) && echo 0 || echo 1
Ich habe noch eine andere Lösung:
if [ "$(echo glob*)" != 'glob*' ]
Das funktioniert gut für mich. Gibt es einige Eckfälle, die ich vermisse?
Basierend auf der Antwort von flabdablet scheint es für mich am einfachsten (nicht unbedingt am schnellsten), find selbst zu verwenden, während die Glob-Erweiterung auf Shell verbleibt, wie:
find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"
Oder in if
wie:
if find $yourGlob -quit &> /dev/null; then
echo "MATCH"
else
echo "NOT-FOUND"
fi
Um die Antwort von MYYN etwas zu vereinfachen, basierend auf seiner Idee:
M=(*py)
if [ -e ${M[0]} ]; then
echo Found
else
echo Not Found
fi
Dieser Greuel scheint zu funktionieren:
#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
echo "Glob matched"
else
echo "Glob did not match"
fi
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 Echobefehls an, dass der Glob etwas stimmte.
In Bash können Sie zu einem Array globieren. Wenn der Glob nicht übereinstimmt, enthält Ihr Array einen einzelnen Eintrag, der keiner vorhandenen Datei entspricht:
#!/bin/bash
shellglob='*.sh'
scripts=($shellglob)
if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi
Hinweis: Wenn Sie nullglob
eingestellt haben, ist scripts
ein leeres Array, und Sie sollten stattdessen mit [ "${scripts[*]}" ]
oder mit [ "${#scripts[*]}" != 0 ]
testen. Wenn Sie eine Bibliothek schreiben, die mit oder ohne nullglob
funktionieren muss, müssen Sie dies tun
if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]
Ein Vorteil dieses Ansatzes besteht darin, dass Sie über die Liste der Dateien verfügen, mit denen Sie arbeiten möchten, anstatt die Glob-Operation wiederholen zu müssen.
Ich habe diese Antwort nicht gesehen.
set -- glob*
[ -f "$1" ] && echo "found [email protected]"
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
echo "I found ${FOUND} matches"
else
echo "No matches found"
fi
if ls -d $glob > /dev/null 2>&1; then
echo Found.
else
echo Not found.
fi
Beachten Sie, dass dies sehr zeitaufwändig sein kann, wenn viele Übereinstimmungen vorhanden sind oder der Dateizugriff langsam ist.
[ls glob* 2>/dev/null | head -n 1
] && echo true
set -- glob*
if [ -f "$1" ]; then
echo "It matched"
fi
Wenn keine Übereinstimmung für glob*
vorhanden ist, enthält $1
'glob*'
. Der Test -f "$1"
ist nicht wahr, da die glob*
-Datei nicht vorhanden ist.
Dies funktioniert mit sh und Derivaten: ksh und bash. Es erstellt keine Sub-Shell. $(..)
- und `...`
-Befehle erstellen eine Sub-Shell. Sie verzweigen einen Prozess und sind daher langsamer als diese Lösung.