Unter
[ -f "$file" ]
le site [
Befehl führt eine stat()
(nicht lstat()
) Systemaufruf auf dem Pfad, der in $file
und gibt zurück wahr wenn dieser Systemaufruf erfolgreich ist und der Typ der Datei, wie er von stat()
ist " regelmäßig ".
Wenn also [ -f "$file" ]
true zurückgibt, können Sie feststellen, dass die Datei tatsächlich existiert und eine reguläre Datei oder ein Symlink ist, der letztendlich zu einer regulären Datei führt (oder zumindest zum Zeitpunkt der stat()
).
Wenn sie jedoch Folgendes zurückgibt falsch (oder wenn [ ! -f "$file" ]
o ! [ -f "$file" ]
return true), gibt es viele verschiedene Möglichkeiten:
- die Datei existiert nicht
- die Datei existiert, ist aber keine regelmäßig Datei (kann ein Gerät, Fifo, Verzeichnis, Socket ... sein)
- die Datei existiert, aber Sie haben keine Suchberechtigung für das übergeordnete Verzeichnis
- die Datei existiert, aber der Pfad zum Zugriff auf sie ist zu lang
- die Datei ist ein Symlink zu einer regulären Datei, aber Sie haben keine Suchberechtigung für einige der Verzeichnisse, die an der Auflösung des Symlinks beteiligt sind.
- ... einen anderen Grund, warum die
stat()
Systemaufruf kann fehlschlagen.
Kurz gesagt, so sollte es sein:
if [ -f "$file" ]; then
printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi
Um sicher zu sein, dass die Datei nicht existiert, brauchen wir die stat()
Systemaufruf mit einem Fehlercode von ENOENT
( ENOTDIR
sagt uns, dass eine der Pfadkomponenten kein Verzeichnis ist (ein weiterer Fall, in dem wir anhand des Pfades feststellen können, dass die Datei nicht existiert). Leider ist die [
Befehl lässt uns das nicht wissen. Er gibt false zurück, unabhängig davon, ob der Fehlercode ENOENT, EACCESS (Zugriff verweigert), ENAMETOOLONG oder ein anderer ist.
Le site [ -e "$file" ]
Test kann auch durchgeführt werden mit ls -Ld -- "$file" > /dev/null
. In diesem Fall, ls
wird Ihnen sagen, warum die stat()
fehlgeschlagen, obwohl die Informationen nicht ohne weiteres programmatisch verwendet werden können:
$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed
Zumindest ls
sagt mir, dass es nicht daran liegt, dass die Datei nicht existiert, dass es nicht klappt. Es liegt daran, dass es nicht feststellen kann, ob die Datei existiert oder nicht. Die [
Der Befehl hat das Problem einfach ignoriert.
Mit dem zsh
Shell, können Sie den Fehlercode mit dem Befehl $ERRNO
spezielle Variable nach der fehlgeschlagenen [
und dekodieren diese Nummer mit dem Befehl $errnos
speziellen Array in der zsh/system
Modul:
zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
err=$ERRNO
case $errnos[err] in
("") echo exists, not a regular file;;
(ENOENT|ENOTDIR)
if [ -L "$file" ]; then
echo broken link
else
echo does not exist
fi;;
(*) syserror -p "can't tell: " "$err"
esac
fi
(Vorsicht vor dem $errnos
Unterstützung war bei einigen Versionen von zsh
wenn sie mit aktuellen Versionen von gcc
).
226 Stimmen
Ich habe dies gefunden Liste der Bash-Bedingungsanweisungen sehr nützlich.
11 Stimmen
Da ich ein sehr fauler Mensch bin, hätte ich normalerweise die folgende alberne Umgehungskonstruktion verwendet:
if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;
Wahrscheinlich ist es gut, dass ich stattdessen diese Frage gefunden und gelernt habe, wie man es richtig macht :)7 Stimmen
Der Einfachheit halber sollten Sie "reguläre Datei" sagen, da die meisten UNIX/POSIX-Dokumente alle Arten von Dateisystemeinträgen einfach als "Dateien" bezeichnen, z. B. ist ein symbolischer Link eine Art von Datei, ebenso wie eine benannte Pipe, eine reguläre Datei, ein Verzeichnis, ein Block-Special, ein Character-Special, ein Socket usw.
12 Stimmen
@kevinarpe wenn Sie testen wollen, ob etwas existiert, verwenden Sie
-e
. -f erkennt keine Verzeichnisse, Symlinks usw.15 Stimmen
Um sicher zu gehen, verwenden Sie immer doppelte Anführungszeichen, um Dateinamen mit Leerzeichen korrekt zu behandeln, z.B.,
FILE=$1
->FILE="$1"
etif [ -f $FILE ];
->if [ -f "$FILE" ];
0 Stimmen
Leider ist dies anfällig für Race Conditions und in den meisten Anwendungsfällen nicht sinnvoll, abgesehen von trivialen Hallo-Welt-Beispielen.
1 Stimmen
Als Randbemerkung und in Bezug auf Ihre Variable "$FILE": Konventionell werden Umgebungsvariablen (PATH, EDITOR, SHELL, ...) und interne Shell-Variablen (BASH_VERSION, RANDOM, ...) großgeschrieben. Alle anderen Variablennamen sollten klein geschrieben werden. Da bei Variablennamen zwischen Groß- und Kleinschreibung unterschieden wird, verhindert diese Konvention, dass Umgebungsvariablen und interne Variablen versehentlich überschrieben werden.
0 Stimmen
Mögliches Duplikat von Bash: Wie prüfe ich, ob bestimmte Dateien existieren?
3 Stimmen
@jww Dies ist das Gegenteil dieser Frage. Hier wird nach einer bestimmten Syntax gefragt.