Jak sprawdzić, czy zwykły plik nie istnieje w Bash?


3260

Użyłem następującego skryptu, aby sprawdzić, czy plik istnieje:

#!/bin/bash

FILE=$1     
if [ -f $FILE ]; then
   echo "File $FILE exists."
else
   echo "File $FILE does not exist."
fi

Jakiej poprawnej składni użyć, jeśli chcę tylko sprawdzić, czy plik nie istnieje?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi

187
Uważam, że ta lista instrukcji warunkowych bash jest bardzo przydatna.
Saulius Žemaitaitis

9
Będąc bardzo leniwą osobą, którą zwykle jestem, zwykle używałbym następującej głupiej metody obejścia: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Prawdopodobnie dobrze, że znalazłem to pytanie i nauczyłem się robić to we właściwy sposób. :)
Alderath,

5
Aby być pendantic, powinieneś powiedzieć „zwykły plik”, ponieważ większość dokumentów UNIX / POSIX ogólnie odnosi się do wszystkich typów wpisów w systemie plików, po prostu „pliki”, np. Dowiązanie symboliczne jest rodzajem pliku, podobnie jak nazwany potok , zwykły plik, katalog, specjalne blokowanie, specjalne
znakowanie

11
@kevinarpe, jeśli chcesz sprawdzić, czy coś istnieje, użyj -e. -f nie odbiera katalogów, dowiązań symbolicznych itp.
Benubird

14
Aby być bezpiecznym, zawsze używaj podwójnych cudzysłowów, aby poprawnie obsługiwać nazwy plików z białymi FILE=$1FILE="$1"if [ -f $FILE ];if [ -f "$FILE" ];
spacjami

Odpowiedzi:


4520

Polecenie testowe ( [tutaj) ma operator logiczny „nie”, który jest wykrzyknikiem (podobnie jak wiele innych języków). Spróbuj tego:

if [ ! -f /tmp/foo.txt ]; then
    echo "File not found!"
fi

208
Bardziej zwięźle: [! -f /tmp/foo.txt] && echo „Nie znaleziono pliku!”
DavidWinterbottom

38
Nie mogłem znaleźć właściwej składni dla „jeśli któryś z 2 plików nie istnieje”. Oba działają:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk

153
@DavidWinterbottom Jeszcze bardziej udany:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.

27
Parametr może być dowolny z poniższych:-e: Returns true value, if file exists -f: Return true value, if file exists and regular file -r: Return true value, if file exists and is readable -w: Return true value, if file exists and is writable -x: Return true value, if file exists and is executable -d: Return true value, if exists and is a directory
SD.

5
Występuje asymetria w użyciu ! -fz &&kontra -fz ||. Ma to związek z kodem wyjścia zwróconym przez kontrolę braku / istnienia. Jeśli potrzebujesz, aby twoja linia zawsze wychodziła czysto z kodem wyjścia 0 (a czasem nie chcesz tego ograniczenia), te dwa podejścia nie są wymienne. Alternatywnie, po prostu użyj ifinstrukcji i nie musisz się już martwić kodem wyjścia z kontroli braku / istnienia.
Acumenus

670

Testowanie plików bash

-b filename- Blokowanie pliku specjalnego
-c filename- Plik znaków specjalnych
-d directoryname- Sprawdzanie istnienia katalogu
-e filename- Sprawdzanie istnienia pliku, niezależnie od typu (węzeł, katalog, gniazdo itp.)
-f filename- Sprawdzanie regularnego istnienia pliku, a nie katalogu
-G filename- Sprawdzanie, czy plik istnieje i jest własnością efektywny identyfikator grupy
-G filename set-group-id- Prawda, jeśli plik istnieje i ma ustawiony identyfikator grupy
-k filename- Bit lepki
-L filename- Łącze symboliczne
-O filename- Prawda, jeśli plik istnieje i jest własnością efektywnego identyfikatora użytkownika
-r filename- Sprawdź, czy plik jest czytelny
-S filename- Sprawdź, czy plik jest gniazdem
-s filename- Sprawdź, czy rozmiar pliku jest różny od zera
-u filename- Sprawdź, czy ustawiony jest bit pliku set-user-id
-w filename- Sprawdź, czy plik jest zapisywalny
-x filename- Sprawdź, czy plik jest wykonywalny

Jak używać:

#!/bin/bash
file=./file
if [ -e "$file" ]; then
    echo "File exists"
else 
    echo "File does not exist"
fi 

Ekspresja Test może być zniesiona przez pomocą !operatora

#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
    echo "File does not exist"
else 
    echo "File exists"
fi 

1
@ 0x90 Jeśli chcesz, możesz edytować mój post i dodać go do listy. Chyba masz na myśli: -n String- Sprawdź, czy długość łańcucha nie jest równa zero. Czy masz na myśli file1 -nt file2- Sprawdź, czy plik 1 jest nowszy niż plik 2 (możesz też użyć opcji -ot dla starszych)
BlueCacti

1
O -n: The unary operator -z tests for a null string, while -n or no operator at all returns True if a string is not empty.~ ibm.com/developerworks/library/l-bash-test/index.html
BlueCacti

1
dlaczego niektórzy nie dodali funkcji takiej jak funkcja istnieje () {⏎ jeśli [-e "$ 1"]; następnie echo „1 $ istnieje” inaczej echo „1 $ nie istnieje” fi}
MZ A

291

Możesz zanegować wyrażenie za pomocą „!”:

#!/bin/bash
FILE=$1

if [ ! -f "$FILE" ]
then
    echo "File $FILE does not exist"
fi

Odpowiednią stroną podręcznika jest man testlub, odpowiednio, man [- help testlub help [dla wbudowanego polecenia bash.


4
W bash , [jest wbudowana. Odpowiednia informacja jest więc uzyskiwana raczej przez help [... ale to pokazuje, że [jest to synonim testwbudowanego, stąd odpowiednia informacja jest uzyskiwana przez help test. Zobacz także sekcję Wyrażenie warunkowe Bash w podręczniku .
gniourf_gniourf

@gniourf_gniourf: Tak, ale bash wbudowaną [zachowuje się bardzo podobnie do polecenia zewnętrznego [polecenia, więc albo man testczy man [daje dobry pomysł, jak to działa.
Keith Thompson,

8
@KeithThompson z wyjątkiem tego, że wbudowane bash[ ma więcej przełączników niż polecenie zewnętrzne [znalezione w moim systemie ... Ogólnie mówiąc, uważam, że lepiej jest przeczytać dokumentację dotyczącą danego narzędzia, a nie dokumentację dotyczącą innego, niejasno powiązanego. Mogę się jednak mylić;)
gniourf_gniourf

134
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

Możliwe też, że plik jest zepsutym dowiązaniem symbolicznym lub nieregularnym plikiem, takim jak np. Gniazdo, urządzenie lub fifo. Na przykład, aby dodać sprawdzanie uszkodzonych dowiązań symbolicznych:

if [[ ! -f $FILE ]]; then
    if [[ -L $FILE ]]; then
        printf '%s is a broken symlink!\n' "$FILE"
    else
        printf '%s does not exist!\n' "$FILE"
    fi
fi

9
Czy mogę zapytać, dlaczego dwa „[” są w teście? (np. [[! -a $ FILE]]). Wypróbowałem wszystkie opcje wymienione na pudełku solaris i tylko ta działała, bardzo wdzięczna, ale dlaczego?
Dimitrios Mistriotis,

30
Podwójne nawiasy to „nowoczesne” rozszerzenie; np. nie będą dzielić słów (na przykład w nazwach plików ze spacjami) i nadal będą pracować dla pustych ciągów: mywiki.wooledge.org/BashFAQ/031
bw1024

7
według tldp.org/LDP/abs/html/fto.html -a działa identycznie jak -e. Został on „przestarzały”, a jego stosowanie jest odradzane. W każdym razie +1 za wzmiankę o sprawdzeniu zepsutego dowiązania symbolicznego
Luca Borrione 1'12

4
@dimitrismistriotis two „[” to nieprzenośne rozszerzenie zaimplementowane (inaczej) przez zsh & bash; generalnie powinieneś tego unikać, jeśli to w ogóle możliwe.
Good Person

7
Głosowałem za tym, ponieważ użyłem [[. Jest to szeroko stosowane rozszerzenie. Jeśli wiesz, że używasz bash, nie ma powodu, aby go nie używać. Jest znacznie mniej podatny na błędy niż [.
Michael Potter

101

Warto wspomnieć, że jeśli chcesz wykonać jedno polecenie, możesz je skrócić

if [ ! -f "$file" ]; then
    echo "$file"
fi

do

test -f "$file" || echo "$file"

lub

[ -f "$file" ] || echo "$file"

Dzięki! Potrzebowałem alternatywy, która nie korzysta z [[]]
Jonathan

69

Wolę zrobić następującą linijkę w formacie zgodnym z powłoką POSIX :

$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"

$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"

Dla kilku poleceń, tak jak zrobiłbym to w skrypcie:

$  [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}

Kiedy zacząłem to robić, rzadko używam już w pełni wpisanej składni !!


1
Po pierwsze, niecytowane odwołania do zmiennych są podatne na błędy. Powiedział, gdzie jest napisane w dowolnym manpage bash, że [albo testwbudowaną by przetestować dla istnienia plików argumentu domyślnie (w przeciwieństwie do -e)? Czy nie byłoby to dwuznaczne? AFAIK (i AIUI sekcja „WARUNKOWE WYRAŻENIA”), jedyną rzeczą, która jest testowana z podejścia jest to, że argument nie jest pusty (lub nieokreślony), który jest w tym przypadku tautologią (niech $DIR = ''a $FILE = '', to argument jest nadal '//').
PointedEars,

1
Dowód: ls /foowynik ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42wynik 42. GNU bash, wersja 4.2.37 (1) -release (i486-pc-linux-gnu).
PointedEars,

@PointedEars: Nie określiłem -fopcji, w chwili pisania tej odpowiedzi. Oczywiście zawsze możesz użyć -e, jeśli nie jesteś pewien, czy będzie to zwykły plik. Dodatkowo we wszystkich moich skryptach cytuję te konstrukcje, musiałem to właśnie przesłać bez odpowiedniego potwierdzenia.
JM Becker,

ACK. Ale zapewne wiesz, że jedna linijka nie może rozwiązać problemu „jeśli-inaczej”: [ $condition ] && if_true || if_falsejest podatna na błędy. W każdym razie [ ! -f "$file" ] && if_not_existsłatwiej mi je przeczytać i zrozumieć niż [ -f "$file" ] || if_not_exists.
PointedEars,

55

Aby przetestować istnienie pliku, parametrem może być dowolny z poniższych:

-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink

Wszystkie poniższe testy dotyczą zwykłych plików, katalogów i dowiązań symbolicznych:

-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0

Przykładowy skrypt:

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi

39

Możesz to zrobić:

[[ ! -f "$FILE" ]] && echo "File doesn't exist"

lub

if [[ ! -f "$FILE" ]]; then
    echo "File doesn't exist"
fi

Jeśli chcesz sprawdzić zarówno plik, jak i folder, użyj -eopcji zamiast -f. -ezwraca true dla zwykłych plików, katalogów, gniazd, znaków specjalnych, blokowania plików specjalnych itp.


1
To nie jest specyficzne dla bash. Składnia pochodzi z powłoki Korna i jest również dostępna w Zsh i Bash. Ma jednak ograniczoną przewagę nad standardową [użytecznością.
Stephane Chazelas

zwykłe pliki (sprawdzane przez -f) i katalogi to tylko dwa z wielu różnych typów plików . Istnieją również gniazda, dowiązania symboliczne, urządzenia, fifos, drzwi ... [ -esprawdzą istnienie pliku (dowolnego typu, w tym zwykły, fifo, katalog ...) po rozwiązaniu symlink .
Stephane Chazelas

@StephaneChazelas: Nigdzie nie widzę tagów ksh ani zsh. Mówimy o bash lub czymś, co jest standaryzowane w większości systemów uniksowych. Tak więc w kontekście jest specyficzny dla bash. OP nie musi wiedzieć o wszystkich dostępnych pociskach: D
Jahid

To był komentarz do części twojej odpowiedzi „Bash specific” .
Stephane Chazelas

@StephaneChazelas: Tak, w kontekście (bash i standardowy sh), jest specyficzny dla bash. Nie widzę sensu w twoim drugim komentarzu, jest całkowicie poza kontekstem. OP nie potrzebuje -e, on potrzebuje -f.
Jahid

35

Należy zachować ostrożność podczas uruchamiania testzmiennej niecytowanej, ponieważ może ona generować nieoczekiwane wyniki:

$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1

Zazwyczaj zaleca się otoczenie testowanej zmiennej podwójnymi cudzysłowami:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi

8
Zaleca się, aby każdą zmienną otaczać podwójnymi cudzysłowami, chyba że wiesz dokładnie, że masz jeden z rzadkich przypadków, w których jest to niepotrzebne, lub jeden z jeszcze rzadszych przypadków, w których jest szkodliwy. (I nie, to nie jest jeden z nich.)
Uwe

Czy chciałbyś wyjaśnić, dlaczego nie jest tak w przypadku podwójnego cudzysłowu? W przeciwnym razie nie widzę przydatności w komentarzu.
artdanil

4
Miałem na myśli: To nie jest jeden z rzadkich przypadków, gdy jest to niepotrzebne lub szkodliwe. Programista powłoki powinien przyzwyczaić się do zamykania (prawie) każdej zmiennej w cudzysłowie; zasada ta nie jest ograniczona do [ ... ].
Uwe


24

Można to zrobić na trzy różne sposoby:

  1. Neguj status wyjścia za pomocą bash (żadna inna odpowiedź nie powiedziała tego):

    if ! [ -e "$file" ]; then
        echo "file does not exist"
    fi

    Lub:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Neguj test w poleceniu testowym [(w taki sposób prezentowało większość odpowiedzi wcześniej):

    if [ ! -e "$file" ]; then
        echo "file does not exist"
    fi

    Lub:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Działaj na wynik testu ujemny ( ||zamiast &&):

    Tylko:

    [ -e "$file" ] || echo "file does not exist"

    Wygląda to głupio (IMO), nie używaj go, chyba że twój kod musi być przenośny do powłoki Bourne'a (takiej jak /bin/shSolaris 10 lub wcześniejsza), w której brakowało operatora negacji potoku ( !):

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi

Jakaś różnica w przenośności między ! [i [ !?
Frozen Flame

3
! [ Jest POSIX dla rurociągów osłonowych 2.9.2 (dowolny poleceń) Otherwise, the exit status shall be the logical NOT of the exit status of the last command i [ ! jest POSIX dla testu ! expression True if expression is false. False if expression is true. Więc oba są POSIX, w moim doświadczeniu, oba są szeroko obsługiwane.

1
@FrozenFlame, powłoka Bourne'a nie miała !słowa kluczowego wprowadzonego przez powłokę Korna. Jednak z wyjątkiem Solaris 10 i starszych, prawdopodobnie nie spotkasz się teraz z powłoką Bourne'a.
Stephane Chazelas

22

W

[ -f "$file" ]

[komenda robi stat()(nie lstat()) wywołanie systemowe na ścieżce przechowywanej w $filei zwraca prawda jeśli się powiedzie, że wywołanie systemowe i typ pliku jako zwrócony przez stat()to „ regular ”.

Więc jeśli [ -f "$file" ]zwróci true, możesz powiedzieć, że plik istnieje i jest zwykłym plikiem lub dowiązaniem symbolicznym, które ostatecznie przekształca się w zwykły plik (lub przynajmniej tak było w momencie stat()).

Jeśli jednak zwraca false (lub jeśli [ ! -f "$file" ]lub ! [ -f "$file" ]zwraca true), istnieje wiele różnych możliwości:

  • plik nie istnieje
  • plik istnieje, ale nie jest zwykłym plikiem (może to być urządzenie, fifo, katalog, gniazdo ...)
  • plik istnieje, ale nie masz uprawnień do wyszukiwania w katalogu nadrzędnym
  • plik istnieje, ale ścieżka dostępu do niego jest za długa
  • plik jest dowiązaniem symbolicznym do zwykłego pliku, ale nie masz uprawnień do wyszukiwania do niektórych katalogów zaangażowanych w rozwiązywanie dowiązania symbolicznego.
  • ... z jakiegokolwiek innego powodu, dla którego stat()wywołanie systemowe może zakończyć się niepowodzeniem.

Krótko mówiąc, powinno to być:

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

Aby mieć pewność, że plik nie istnieje, potrzebujemy stat()wywołania systemowego z kodem błędu ENOENT( ENOTDIRmówi nam, że jeden ze składników ścieżki nie jest katalogiem, to kolejny przypadek, w którym możemy stwierdzić, że plik nie istnieje istnieje przy tej ścieżce). Niestety [polecenie to nas nie informuje. Zwróci false, czy kod błędu to ENOENT, EACCESS (odmowa zgody), ENAMETOOLONG lub cokolwiek innego.

[ -e "$file" ]Test może być również wykonane z ls -Ld -- "$file" > /dev/null. W takim przypadku lspowie Ci, dlaczego stat()nie powiodło się, chociaż informacji nie można łatwo wykorzystać programowo:

$ 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

Przynajmniej lsmówi mi, że to nie dlatego, że plik nie istnieje, że zawodzi. Jest tak, ponieważ nie można stwierdzić, czy plik istnieje, czy nie. [Komenda prostu zignorował problem.

Za pomocą zshpowłoki można wyszukać kod błędu za pomocą $ERRNOspecjalnej zmiennej po nieudanym [poleceniu i zdekodować tę liczbę za pomocą $errnosspecjalnej tablicy w zsh/systemmodule:

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

(uwaga: $errnosobsługa została zerwana z niektórymi wersjami zshpo zbudowaniu z najnowszymi wersjamigcc ).


11

Aby odwrócić test, użyj „!”. Jest to równoważne operatorowi logicznemu „nie” w innych językach. Spróbuj tego:

if [ ! -f /tmp/foo.txt ];
then
    echo "File not found!"
fi

Lub napisane w nieco inny sposób:

if [ ! -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Lub możesz użyć:

if ! [ -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Lub wyciskając wszystko razem:

if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi

Które można zapisać (używając wtedy „i” operator: &&) jako:

[ ! -f /tmp/foo.txt ] && echo "File not found!"

Który wygląda tak krócej:

[ -f /tmp/foo.txt ] || echo "File not found!"


10

Ten kod również działa.

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi

7

Najprostszy sposób

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"

6

Ten skrypt powłoki działa również w celu znalezienia pliku w katalogu:

echo "enter file"

read -r a

if [ -s /home/trainee02/"$a" ]
then
    echo "yes. file is there."
else
    echo "sorry. file is not there."
fi

3
Nie jest jasne, czy dodaje to wszystko, czego inne odpowiedzi jeszcze nie dały. Twarde koduje nazwę ścieżki. Ponieważ pytanie jest oznaczone jako bash , może służyć zarówno read -p "Enter file name: " -r ado monitowania, jak i czytania. Używa cudzysłowów wokół zmiennej; to dobrze, ale należy to wyjaśnić. Może być lepiej, jeśli powtórzy nazwę pliku. I to sprawdza, czy plik istnieje i nie jest pusty (to jest znaczenie -s), podczas gdy pytanie dotyczy dowolnego pliku, pustego lub nie (dla czego -fbardziej odpowiednie).
Jonathan Leffler,

4

czasami przydatne może być użycie && i || operatorzy.

Jak w (jeśli masz polecenie „test”):

test -b $FILE && echo File not there!

lub

test -b $FILE || echo File there!

2

Jeśli chcesz użyć testzamiast [], możesz użyć, !aby uzyskać negację:

if ! test "$FILE"; then
  echo "does not exist"
fi

0

Możesz również pogrupować wiele poleceń w jednym wierszu

[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )

lub

[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}

Jeśli nazwa pliku nie zostanie zakończona, wynikiem będzie

test1
test2
test3

Uwaga: (...) działa w podpowłoce, {...;} działa w tej samej powłoce. Notacja w nawiasach klamrowych działa tylko w trybie bash.


1
Nie, notacja w nawiasach klamrowych to nie tylko bash; działa w dowolnej powłoce zgodnej z POSIX.
Charles Duffy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.