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.