Come faccio a sapere se un file normale non esiste in Bash?


3267

Ho usato il seguente script per vedere se esiste un file:

#!/bin/bash

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

Qual è la sintassi corretta da usare se voglio solo verificare se il file non esiste?

#!/bin/bash

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

187

9
Essendo la persona molto pigra che sono, in genere avrei usato il seguente costrutto sciocco: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Probabilmente bene che ho trovato questa domanda e ho imparato a farlo in un modo più corretto. :)
Alderath,

5
Per essere pendenti, dovresti dire "file normale", poiché la maggior parte dei documenti UNIX / POSIX fa riferimento genericamente a tutti i tipi di voci del file system semplicemente "file", ad es. Un collegamento simbolico è un tipo di file, come è chiamato pipe , file normale, directory, blocco speciale, carattere speciale, socket, ecc.
kevinarpe

11
@kevinarpe se vuoi verificare se esiste qualcosa , usa -e. -f non raccoglierà directory,
collegamenti

14
Per sicurezza, usa sempre le doppie virgolette per gestire correttamente i nomi dei file con spazi bianchi, ad es. FILE=$1-> FILE="$1"e if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Risposte:


4527

Il comando test ( [qui) ha un operatore logico "non" che è il punto esclamativo (simile a molte altre lingue). Prova questo:

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

208
Più succintamente: [! -f /tmp/foo.txt] && echo "File non trovato!"
DavidWinterbottom,

38
Ho faticato un po 'a trovare la sintassi giusta per "se non esiste uno dei 2 file". Entrambi funzionano come segue:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk,

153
@DavidWinterbottom Ancora più succulento:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.

27
Il parametro può essere uno dei seguenti:-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
C'è un'asimmetria nell'uso ! -fcon &&rispetto all'uso -fcon ||. Ciò ha a che fare con il codice di uscita restituito dal controllo di non esistenza. Se hai bisogno che la tua linea esca sempre in modo pulito con il codice di uscita 0 (e talvolta non vuoi questo vincolo), i due approcci non sono intercambiabili. In alternativa, usa semplicemente ifun'istruzione e non dovrai più preoccuparti del codice di uscita del tuo controllo di non esistenza.
Acumenus

670

Test dei file Bash

-b filename- Blocca file speciale
-c filename- File di caratteri speciali
-d directoryname- Controlla l'esistenza di directory
-e filename- Controlla l'esistenza del file, indipendentemente dal tipo (nodo, directory, socket, ecc.)
-f filename- Controlla l'esistenza di file regolari non una directory
-G filename- Controlla se il file esiste ed è di proprietà di ID gruppo effettivo
-G filename set-group-id- Vero se il file esiste ed è set-group-id
-k filename- Bit appiccicoso
-L filename- Collegamento simbolico
-O filename- Vero se il file esiste ed è di proprietà dell'ID utente effettivo
-r filename- Verifica se il file è leggibile
-S filename- Verifica se il file è socket
-s filename- Verifica se il file ha dimensioni diverse da zero
-u filename- Controlla se il file set-user-id bit è impostato
-w filename- Controlla se il file è scrivibile
-x filename- Controlla se il file è eseguibile

Come usare:

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

Un'espressione di test può essere negato utilizzando l' !operatore

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

1
@ 0x90 Se lo desideri, sei libero di modificare il mio post e aggiungerlo all'elenco. Immagino che intendi: -n String- Controlla se la lunghezza della stringa non è zero. O vuoi dire file1 -nt file2- Controlla se file1 è più recente del file 2 (puoi anche usare -ot per i vecchi allora)
BlueCacti

1
Informazioni su -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
perché alcuni non hanno aggiunto una funzione come la funzione esiste () {⏎ if [-e "$ 1"]; allora l'eco "$ 1 esiste" altrimenti l'eco "$ 1 non esiste" fi}
Mz A

291

Puoi negare un'espressione con "!":

#!/bin/bash
FILE=$1

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

La pagina man rilevante è man testo, equivalentemente, man [- help testo help [per il comando bash incorporato.


4
In bash , [è incorporato. Quindi le informazioni rilevanti sono piuttosto ottenute da help [... ma questo dimostra che [è sinonimo di testincorporato, quindi le informazioni rilevanti sono piuttosto ottenute da help test. Vedi anche la sezione Espressione condizionale di Bash nel manuale .
gniourf_gniourf,

@gniourf_gniourf: Sì, ma il [comando integrato bash si comporta in modo molto simile al [comando esterno , quindi man testo man [ti darà una buona idea di come funziona.
Keith Thompson,

8
@KeithThompson, tranne per il fatto che il built-in bash[ ha più switch rispetto al comando esterno [trovato sul mio sistema ... In generale, credo che sia meglio leggere la documentazione specifica di un determinato strumento, e non la documentazione specifica di un altro vagamente correlato. Potrei sbagliarmi, però;)
gniourf_gniourf,

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

Inoltre, è possibile che il file sia un collegamento simbolico interrotto o un file non regolare, come ad esempio un socket, un dispositivo o un FIFO. Ad esempio, per aggiungere un controllo per collegamenti simbolici non funzionanti:

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
Posso chiederti perché i due "[" s nel test? (ad es. [[! -a $ FILE]]). Ho provato tutte le opzioni menzionate su una scatola di solaris e solo quella ha funzionato, così riconoscente, ma perché?
Dimitrios Mistriotis,

30
Le doppie parentesi sono un'estensione "moderna"; ad esempio, non eseguiranno la suddivisione delle parole (come per i nomi di file con spazi) e funzioneranno comunque per stringhe vuote: mywiki.wooledge.org/BashFAQ/031
bw1024

7
secondo tldp.org/LDP/abs/html/fto.html -a è identico in effetti a -e. È stato "deprecato" e il suo uso è scoraggiato. Comunque +1 per menzionare di controllare anche il collegamento simbolico interrotto
Luca Borrione

4
@dimitrismistriotis two "[" è un'estensione non portatile implementata (diversamente) da zsh & bash; generalmente dovresti evitarlo, se possibile.
Brava persona,

7
Ho votato a favore PERCHÉ ha usato [[. È un'estensione ampiamente utilizzata. Se sai che stai usando bash, non c'è motivo di non usarlo. È molto meno soggetto a errori rispetto a [.
Michael Potter,

101

Vale la pena ricordare che se è necessario eseguire un singolo comando è possibile abbreviare

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

per

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

o

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

Grazie! Era necessaria un'alternativa che non utilizza [[]]
Jonathan il

69

Preferisco fare il seguente one-liner, in formato compatibile con shell POSIX :

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

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

Per un paio di comandi, come farei in uno script:

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

Una volta che ho iniziato a fare questo, raramente uso più la sintassi completamente digitata !!


1
Innanzitutto, i riferimenti alle variabili non quotati sono soggetti a errori. Detto questo, dove si dice in qualsiasi pagina man di bash che il [o testbuilt-in testerebbe l'esistenza del file dell'argomento di default (al contrario di -e)? Non sarebbe ambiguo? AFAIK (e AIUI la sezione "EXPRESSIONS CONDIZIONALI") l'unica cosa che viene testata con il tuo approccio è che l'argomento non è vuoto (o indefinito), che è, in questo caso, una tautologia (let $DIR = ''e $FILE = '', quindi l'argomento è ancora '//').
Pointed Ears

1
Prova:, ls /foorisultato ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, risultato 42. GNU bash, versione 4.2.37 (1) -release (i486-pc-linux-gnu).
Orecchie appuntite

@PointedEars: non sono riuscito a specificare l' -fopzione, al momento ho scritto questa risposta. Ovviamente potresti sempre usare -e, se non sei sicuro che sarà un file normale. Inoltre, in tutti i miei script cito questi costrutti, devo averlo presentato senza prove adeguate.
JM Becker,

ACK. Ma probabilmente sai che un one-liner non può risolvere il problema if-else: [ $condition ] && if_true || if_falseè soggetto a errori. In ogni caso, trovo [ ! -f "$file" ] && if_not_existspiù facile da leggere e capire di [ -f "$file" ] || if_not_exists.
Orecchie appuntite

55

Per verificare l'esistenza del file, il parametro può essere uno dei seguenti:

-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

Tutti i test seguenti si applicano a file, directory e collegamenti simbolici regolari:

-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

Script di esempio:

#!/bin/bash
FILE=$1

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

39

Puoi farlo:

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

o

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

Se si desidera controllare entrambi i file e le cartelle, utilizzare l' -eopzione anziché -f. -eritorna vero per file regolari, directory, socket, file speciali di caratteri, blocchi di file speciali ecc.


1
Questo non è specifico per Bash. La sintassi viene dalla shell Korn ed è disponibile anche in zsh e bash. Ha comunque un vantaggio limitato rispetto [all'utilità standard qui.
Stephane Chazelas,

i file regolari (come verificato da -f) e le directory sono solo due di molti tipi diversi di file . Ci sono anche socket, symlink, dispositivi, fifo, porte ... [ -everificheranno l'esistenza dei file (di qualsiasi tipo, compresi quelli regolari, fifo, directory ...) dopo la risoluzione del symlink .
Stephane Chazelas,

@StephaneChazelas: non vedo alcun tag ksh o zsh da nessuna parte. Stiamo parlando di bash o di qualcosa che è standardizzato sulla maggior parte dei sistemi unix. Quindi, nel contesto, è specifico per bash. OP non ha bisogno di sapere di qualunque shell ci siano: D
Jahid

Quello era un commento sulla parte "Bash specific" della tua risposta.
Stephane Chazelas,

@StephaneChazelas: Sì, e nel contesto (bash e lo sh standard), è specifico per bash. Non vedo il punto nel tuo secondo commento, è totalmente fuori contesto. OP non ha bisogno -e, ha bisogno -f.
Jahid

35

Dovresti fare attenzione a eseguire testuna variabile non quotata, perché potrebbe produrre risultati imprevisti:

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

La raccomandazione è di solito di avere la variabile testata racchiusa tra virgolette doppie:

#!/bin/sh
FILE=$1

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

8
La raccomandazione è di avere ogni variabile racchiusa tra virgolette doppie, a meno che non si sappia esattamente di avere uno dei rari casi in cui non è necessario o uno dei casi ancora più rari in cui è dannoso. (E no, questo non è uno di questi.)
Uwe,

Ti andrebbe di spiegare perché questo non è il caso di usare le virgolette doppie? Altrimenti non vedo l'utilità nel commento.
artdanil,

4
Intendevo: questo non è uno dei rari casi in cui è inutile o dannoso. Un programmatore di shell dovrebbe abituarsi a racchiudere (quasi) ogni variabile tra virgolette doppie; questa regola non è limitata a [ ... ].
Uwe,


24

Esistono tre modi distinti per farlo:

  1. Annulla lo stato di uscita con bash (nessun'altra risposta ha detto questo):

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

    O:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Annulla il test all'interno del comando test [(questo è il modo in cui la maggior parte delle risposte prima hanno presentato):

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

    O:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Agire sul risultato del test in modo negativo ( ||anziché &&):

    Solo:

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

    Questo sembra sciocco (IMO), non usarlo a meno che il tuo codice non debba essere portabile sulla shell Bourne (come quella /bin/shdi Solaris 10 o precedente) che mancava dell'operatore di negazione della pipeline ( !):

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

Qualche differenza di portabilità tra ! [e [ !?
Frozen Flame,

3
Il ! [ è POSIX per tubazioni shell 2.9.2 (qualsiasi comando) Otherwise, the exit status shall be the logical NOT of the exit status of the last command e [ ! è POSIX per il test ! expression True if expression is false. False if expression is true. Quindi, entrambi sono POSIX, e nella mia esperienza, entrambi sono ampiamente supportati.

1
@FrozenFlame, la shell Bourne non aveva la !parola chiave introdotta dalla shell Korn. Tranne forse per Solaris 10 e precedenti, è improbabile che in questi giorni si verifichi una shell Bourne.
Stephane Chazelas,

23

In

[ -f "$file" ]

il [comando esegue una stat()(non lstat()) chiamata di sistema sul percorso memorizzato $filee restituisce true se la chiamata di sistema ha esito positivo e il tipo di file restituito dastat() è " regolare ".

Quindi se [ -f "$file" ] restituisce true, puoi dire che il file esiste ed è un file normale o un link simbolico che alla fine si risolve in un file normale (o almeno lo era al momento delstat() ).

Tuttavia, se restituisce false (o if[ ! -f "$file" ] o ! [ -f "$file" ]return true), ci sono molte diverse possibilità:

  • il file non esiste
  • il file esiste ma non è un normale file (potrebbe essere un dispositivo, fifo, directory, socket ...)
  • il file esiste ma non si dispone dell'autorizzazione di ricerca nella directory principale
  • il file esiste ma quel percorso per accedervi è troppo lungo
  • il file è un collegamento simbolico a un file normale, ma non si dispone dell'autorizzazione di ricerca per alcune directory coinvolte nella risoluzione del collegamento simbolico.
  • ... qualsiasi altro motivo per cui la stat()chiamata di sistema potrebbe non riuscire.

In breve, dovrebbe essere:

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

Per sapere con certezza che il file non esiste, avremmo bisogno che la stat()chiamata di sistema ritorni con un codice di errore di ENOENT( ENOTDIRci dice che uno dei componenti del percorso non è una directory è un altro caso in cui possiamo dire che il file non esiste da quel percorso). Sfortunatamente il [comando non ce lo fa sapere. Restituirà falso se il codice di errore è ENOENT, EACCESS (autorizzazione negata), ENAMETOOLONG o qualsiasi altra cosa.

Il [ -e "$file" ]test può anche essere eseguito con ls -Ld -- "$file" > /dev/null. In tal caso, lsti dirà perché l' stat()errore non è riuscito, sebbene le informazioni non possano essere facilmente utilizzate a livello di codice:

$ 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

Almeno lsmi dice che non è perché il file non esiste che non riesce. È perché non può dire se il file esiste o no. Il [comando ha semplicemente ignorato il problema.

Con la zshshell, è possibile eseguire una query sul codice di errore con la $ERRNOvariabile speciale dopo il [comando non riuscito e decodificare quel numero utilizzando l' $errnosarray speciale nel zsh/systemmodulo:

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

(attenzione il $errnossupporto è stato interrotto con alcune versioni di zshquando costruito con versioni recenti digcc ).


11

Per invertire un test, utilizzare "!". Ciò equivale all'operatore logico "non" in altre lingue. Prova questo:

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

O scritto in un modo leggermente diverso:

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

Oppure potresti usare:

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

Oppure, presentando tutti insieme:

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

Che può essere scritto (usando poi "e" operatore: &&) come:

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

Che sembra più corto in questo modo:

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


10

Anche questo codice funziona.

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

7

Il modo più semplice

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

6

Questo script di shell funziona anche per trovare un file in una directory:

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
Non è chiaro che questo aggiunge qualcosa che altre risposte non hanno già dato. Codifica in modo rigido un percorso. Dal momento che la domanda è taggata bash , potrebbe essere usata sia read -p "Enter file name: " -r aper chiedere che per leggere. Usa le virgolette attorno alla variabile; va bene, ma dovrebbe essere spiegato. Potrebbe essere meglio se facesse eco al nome del file. E questo controlla che il file esista e non sia vuoto (questo è il significato di -s) mentre la domanda pone su qualsiasi file, vuoto o no (per il quale -fè più appropriato).
Jonathan Leffler il

4

a volte può essere utile usare && e || operatori.

Come in (se hai il comando "test"):

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

o

test -b $FILE || echo File there!

2

Se si desidera utilizzare testinvece di [], è possibile utilizzare !per ottenere la negazione:

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

0

È inoltre possibile raggruppare più comandi in una riga

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

o

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

Se il nome file non viene chiuso, l'output sarà

test1
test2
test3

Nota: (...) viene eseguito in una subshell, {...;} viene eseguito nella stessa shell. La notazione parentesi graffa funziona solo in bash.


1
No, la notazione della parentesi graffa non è solo bash; funziona in qualsiasi shell conforme a POSIX.
Charles Duffy,

0
envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi

Fornisci una spiegazione insieme al codice.
Zohar

Sebbene questo codice possa risolvere il problema del PO, è meglio includere una spiegazione su come il codice risolve il problema del PO. In questo modo, i futuri visitatori possono imparare dal tuo post e applicarlo al proprio codice. SO non è un servizio di codifica, ma una risorsa per la conoscenza. Inoltre, è più probabile che vengano votate risposte complete e di alta qualità. Queste caratteristiche, insieme alla necessità che tutti i post siano autonomi, sono alcuni dei punti di forza di SO come piattaforma, che lo differenzia dai forum. È possibile modificare per aggiungere ulteriori informazioni e / o integrare le proprie spiegazioni con la documentazione di origine.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.