Jak sprawdzić, czy plik istnieje w skrypcie powłoki

175

Chciałbym napisać skrypt powłoki, który sprawdza, czy określony plik archived_sensor_data.jsonistnieje, a jeśli tak, usuwa go. Po http://www.cyberciti.biz/tips/find-out-if-file-exists-with-conditional-expressions.html wypróbowałem następujące rozwiązania:

[-e archived_sensor_data.json] && rm archived_sensor_data.json

Jednak powoduje to błąd

[-e: command not found

kiedy próbuję uruchomić wynikowy test_controllerskrypt za pomocą ./test_controllerpolecenia. Co jest nie tak z kodem?

Kurt Peek
źródło
2
Musisz ustawić jedną lub więcej białych znaków między otwierającym nawiasem kwadratowym „[” i opcją „-e”, tak samo jak między nazwą pliku a zamykającym nawiasem kwadratowym „]”
Konstantin Yaniv,

Odpowiedzi:

350

Brakuje wymaganej przestrzeni między nawiasem a -e:

#!/bin/bash
if [ -e x.txt ]
then
    echo "ok"
else
    echo "nok"
fi
chris01
źródło
12
I w końcu dodaje się dwie spacje, jeden po otwarciu nawiasach kwadratowych i jeden przed jednym zamknięcia: [ -e archived_sensor_data.json ] && rm archived_sensor_data.json. Wydaje się, że skrypt teraz działa.
Kurt Peek
3
Działa to również przy użyciu if [ -e "$1" ](argument wejściowy nazwy pliku).
Edward
2
Główna różnica polega na tym, że używasz skryptów „bash” zamiast skryptów „powłoki”. Zauważ, że pierwszą linią, którą dodałeś był #! / Bin / bash, więc mówisz maszynie, aby używała "bash" zamiast sh. Ponieważ sh nie rozpoznaje argumentu „-e”
Nick Cuevas
29

Oto alternatywna metoda wykorzystująca ls:

(ls x.txt && echo yes) || echo no

Jeśli chcesz ukryć dane wyjściowe, lsaby zobaczyć tylko tak lub nie, przekieruj stdouti stderrdo /dev/null:

(ls x.txt >> /dev/null 2>&1 && echo yes) || echo no
Philip Kirkbride
źródło
1
Ten kod oznacza: „jeśli lssię powiedzie, to jest taki plik, w przeciwnym razie nie ma”. Jeśli się lsnie powiedzie, nie oznacza to, że brakuje tego pliku. To może być inny błąd. Na przykład utwórz plik w katalogu należącym do roota i spróbuj zrobić to lsjako zwykły użytkownik. To się nie powiedzie Permission denied, co nie jest równoważne, że plik nie istnieje.
Tigran
9

Tłem dla mojej rekomendacji rozwiązania jest historia znajomego, który w drugim tygodniu swojej pierwszej pracy wyczyścił pół serwera kompilacji. Więc podstawowym zadaniem jest dowiedzieć się, czy plik istnieje, a jeśli tak, usuńmy go. Ale na tej rzece jest kilka zdradliwych bystrzy:

  • Wszystko jest plikiem.

  • Skrypty mają prawdziwą moc tylko wtedy, gdy rozwiązują ogólne zadania

  • Mówiąc ogólnie, używamy zmiennych

  • Często używamy -f force w skryptach, aby uniknąć ręcznej interwencji

  • A także miłość -r rekurencyjna, aby mieć pewność, że tworzymy, kopiujemy i niszczymy w odpowiednim czasie.

Rozważ następujący scenariusz:

Mamy plik, który chcemy usunąć: filesexists.json

Ta nazwa pliku jest przechowywana w zmiennej

<host>:~/Documents/thisfolderexists filevariable="filesexists.json"

Mamy również zmienną ścieżki, aby uczynić rzeczy naprawdę elastycznymi

<host>:~/Documents/thisfolderexists pathtofile=".."

<host>:~/Documents/thisfolderexists ls $pathtofile

filesexists.json  history20170728  SE-Data-API.pem  thisfolderexists

Zobaczmy więc, czy -erobi to, co powinien. Czy pliki istnieją?

<host>:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $?

0

To robi. Magia.

Jednak co by się stało, gdyby zmienna pliku została przypadkowo oceniona jako nuffin '

<host>:~/Documents/thisfolderexists filevariable=""

<host>:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $?

0

Co? Ma powrócić z błędem ... I to jest początek historii, jak cały ten folder został przypadkowo usunięty

Alternatywą może być przetestowanie pod kątem tego, co rozumiemy jako „plik”

<host>:~/Documents/thisfolderexists filevariable="filesexists.json"

<host>:~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $?

0

Więc plik istnieje ...

<host>:~/Documents/thisfolderexists filevariable=""

<host>:~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $?

1

Więc to nie jest plik i być może nie chcemy usuwać całego katalogu

man test ma do powiedzenia:

-b FILE

       FILE exists and is block special

-c FILE

       FILE exists and is character special

-d FILE

       FILE exists and is a directory

-e FILE

       FILE exists

-f FILE

       FILE exists and is a regular file

...

-h FILE

       FILE exists and is a symbolic link (same as -L)
Lefty G Balogh
źródło
4

Wewnętrznie polecenie rm i tak musi sprawdzać istnienie pliku,
więc po co dodawać kolejny test? Po prostu wystaw

rm filename

i po tym odejdzie, czy tam był, czy nie.
Użyj rm -f, jeśli nie chcesz żadnych wiadomości o nieistniejących plikach.

Jeśli musisz wykonać jakąś akcję, jeśli plik NIE istnieje, musisz to sprawdzić samodzielnie. Na podstawie Twojego przykładowego kodu nie ma to miejsca w tym przypadku.

TG
źródło
1
W rzeczywistości jest to całkiem poprawna odpowiedź na polecenie rm. W przypadku innych poleceń sugeruję raczej testowanie z -e.
warhansen
Tyle że nie o to chodzi w pytaniu.
Kredki
1
Bardzo o to chodzi w pytaniu, jeśli przeczytasz całe pytanie, w tym część „… i (jeśli tak)… je usuniesz”.
TG
1

Jeśli używasz NFS, „test” jest lepszym rozwiązaniem, ponieważ możesz dodać do niego limit czasu na wypadek, gdyby twój NFS nie działał:

time timeout 3 test -f 
/nfs/my_nfs_is_currently_down
real    0m3.004s <<== timeout is taken into account
user    0m0.001s
sys     0m0.004s
echo $?
124   <= 124 means the timeout has been reached

Konstrukcja "[-e moj_plik]" zawiesza się do czasu, gdy NFS znów będzie działał:

if [ -e /nfs/my_nfs_is_currently_down ]; then echo "ok" else echo "ko" ; fi

<no answer from the system, my session is "frozen">
FCA69
źródło
testi [są synonimami. Możesz również użyć timeoutwith [, a testtakże zawiesza się, jeśli NFS się zawiesza.
ten inny facet