¿Cómo puedo saber si un archivo normal no existe en Bash?

3267

He usado el siguiente script para ver si existe un archivo:

#!/bin/bash

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

¿Cuál es la sintaxis correcta para usar si solo quiero verificar si el archivo no existe?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi
Bill el lagarto
fuente
187
He encontrado esta lista de declaraciones condicionales bash muy útiles.
Saulius Žemaitaitis
99
Siendo la persona muy perezosa que soy, normalmente habría utilizado la siguiente construcción tonta de solución: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Probablemente sea bueno que haya encontrado esta pregunta y aprendí a hacerlo de una manera más adecuada. :)
Alderath
55
Para ser pendante, debe decir "archivo normal", ya que la mayoría de los documentos de UNIX / POSIX se refieren genéricamente a todos los tipos de entradas del sistema de archivos, simplemente "archivos", por ejemplo, un enlace simbólico es un tipo de archivo, como una tubería con nombre , archivo regular, directorio, bloque especial, carácter especial, socket, etc.
kevinarpe
11
@kevinarpe si quieres probar si algo existe, úsalo -e. -f no recogerá directorios, enlaces simbólicos, etc.
Benubird
14
Para estar seguro, use siempre comillas dobles para manejar correctamente los nombres de archivo con espacios en blanco, por ejemplo, FILE=$1-> FILE="$1"y if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Respuestas:

4527

El comando de prueba ( [aquí) tiene un operador lógico "no", que es el signo de exclamación (similar a muchos otros idiomas). Prueba esto:

if [ ! -f /tmp/foo.txt ]; then
    echo "File not found!"
fi
John Feminella
fuente
208
Más sucintamente: [! -f /tmp/foo.txt] && echo "¡Archivo no encontrado!"
DavidWinterbottom
38
Me costó un poco encontrar la sintaxis correcta para "si alguno de los 2 archivos no existe". Lo siguiente funciona:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk
153
@DavidWinterbottom Aún más suculentamente:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.
27
El parámetro puede ser cualquiera de los siguientes:-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.
55
Hay una asimetría al usar ! -fcon &&versus usar -fcon ||. Esto tiene que ver con el código de salida devuelto por la verificación de no existencia. Si necesita que su línea siempre salga limpiamente con el código de salida 0 (y a veces no desea esta restricción), los dos enfoques no son intercambiables. Alternativamente, solo use una ifdeclaración y ya no tendrá que preocuparse por el código de salida de su verificación de no existencia.
Acumenus
670

Prueba de archivo bash

-b filename- Bloquear archivo especial
-c filename- Archivo de caracteres especiales
-d directoryname- Verificar existencia de directorio
-e filename- Verificar existencia de archivo, independientemente del tipo (nodo, directorio, socket, etc.)
-f filename- Verificar existencia de archivo normal no un directorio
-G filename- Verificar si el archivo existe y es propiedad de ID de grupo efectivo
-G filename set-group-id- Verdadero si el archivo existe y es set-group-id
-k filename- Bit Sticky
-L filename- Enlace simbólico
-O filename- Verdadero si el archivo existe y es propiedad del ID de usuario efectivo
-r filename- Verifique si el archivo es legible
-S filename- Verifique si el archivo es socket
-s filename- Verifique si archivo es de tamaño distinto de cero
-u filename- Comprobar si el bit archivo de conjunto-user-id se fija
-w filename- Comprobar si el archivo se puede escribir
-x filename- Comprobar si el archivo es ejecutable

Cómo utilizar:

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

Se puede negar una expresión de prueba utilizando el !operador

#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
    echo "File does not exist"
else 
    echo "File exists"
fi 
BlueCacti
fuente
1
@ 0x90 Si lo desea, puede editar mi publicación y agregarla a la lista. Supongo que quieres decir: -n String- Comprueba si la longitud de la cadena no es cero. ¿O quieres decir file1 -nt file2? Comprueba si el archivo 1 es más nuevo que el archivo 2 (también puedes usar -ot para versiones anteriores)
BlueCacti
1
Acerca de -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
¿por qué algunos no agregaron una función como function existe () {⏎ if [-e "$ 1"]; entonces echo "$ 1 existe" de lo contrario echo "$ 1 no existe" fi}
Mz A
291

Puede negar una expresión con "!":

#!/bin/bash
FILE=$1

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

La página de manual relevante es man testo, de manera equivalente, man [- help testo help [para el comando bash incorporado.

starbeamrainbowlabs
fuente
44
En bash , [es un incorporado. Entonces, la información relevante se obtiene más bien por help [... pero esto muestra que [es un sinónimo de la testconstrucción, por lo tanto, la información relevante se obtiene más bien por help test. Consulte también la sección Expresión condicional de Bash en el manual .
gniourf_gniourf
@gniourf_gniourf: Sí, pero la fiesta incorporada [se comporta de mando de manera muy similar a lo externo [de comandos, así que o bien man testo man [le dará una buena idea de cómo funciona.
Keith Thompson
8
@KeithThompson, excepto que el bash incorporado [tiene más interruptores que el comando externo que se [encuentra en mi sistema ... En general, creo que es mejor leer la documentación específica de una herramienta determinada, y no la documentación específica de otra vagamente relacionada. Aunque podría estar equivocado;)
gniourf_gniourf
134
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

Además, es posible que el archivo sea un enlace simbólico roto, o un archivo no regular, como por ejemplo un socket, dispositivo o fifo. Por ejemplo, para agregar un cheque por enlaces simbólicos rotos:

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
pistolas
fuente
99
¿Puedo preguntar por qué los dos "[" s en la prueba? (por ejemplo, [[! -a $ FILE]]). Probé todas las opciones mencionadas en una caja solaris y solo esa funcionó, muy agradecida, pero ¿por qué?
Dimitrios Mistriotis
30
Los corchetes dobles son una extensión "moderna"; por ejemplo, no dividirán palabras (como los nombres de archivo con espacios) y seguirán trabajando para cadenas vacías: mywiki.wooledge.org/BashFAQ/031
bw1024
77
de acuerdo con tldp.org/LDP/abs/html/fto.html -a es idéntico en efecto a -e. Se ha "desaprobado" y se desaconseja su uso. De todos modos +1 por mencionar que también se debe verificar el enlace simbólico roto
Luca Borrione
44
@dimitrismistriotis two "[" es una extensión no portátil implementada (de manera diferente) por zsh & bash; generalmente debe evitarlo si es posible.
Buena persona
77
He votado esto PORQUE se utilizó [[. Es una extensión muy utilizada. Si sabe que está usando bash, entonces no hay razón para no usarlo. Es mucho menos propenso a errores que [.
Michael Potter
101

Vale la pena mencionar que si necesita ejecutar un solo comando puede abreviar

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

a

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

o

[ -f "$file" ] || echo "$file"
Elazar Leibovich
fuente
¡Gracias! Necesitaba una alternativa que no use [[]]
Jonathan
69

Prefiero hacer el siguiente one-liner, en formato compatible con shell POSIX :

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

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

Para un par de comandos, como lo haría en un script:

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

¡Una vez que comencé a hacer esto, rara vez uso la sintaxis completamente tipada!

JM Becker
fuente
1
En primer lugar, las referencias de variables sin comillas son propensas a errores. Dicho esto, ¿dónde dice en cualquier página de manual de bash que el [o testincorporado probaría la existencia del argumento por defecto (en lugar de -e)? ¿No sería eso ambiguo? AFAIK (y AIUI la sección "EXPRESIONES CONDICIONALES") lo único que se prueba con su enfoque es que el argumento no está vacío (o indefinido), que es, en este caso, una tautología ( $DIR = ''y $FILE = ''luego el argumento sigue siendo '//')
PointedEars
1
Prueba: ls /fooresultado ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, resultado 42. GNU bash, versión 4.2.37 (1) -release (i486-pc-linux-gnu).
PointedEars
@PointedEars: no pude especificar la -fopción, en el momento en que escribí esta respuesta. Obviamente, siempre podría usar -e, si no está seguro de que será un archivo normal. Además, en todas mis secuencias de comandos, cito estas construcciones, debo haber presentado esto sin la prueba adecuada.
JM Becker
ACK. Pero probablemente sepa que una línea no puede resolver el problema if-else: [ $condition ] && if_true || if_falsees propenso a errores. En cualquier caso, me resulta [ ! -f "$file" ] && if_not_existsmás fácil de leer y entender que [ -f "$file" ] || if_not_exists.
PointedEars
55

Para probar la existencia del archivo, el parámetro puede ser cualquiera de los siguientes:

-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

Todas las pruebas a continuación se aplican a archivos, directorios y enlaces simbólicos normales:

-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 de ejemplo:

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi
DAKOTA DEL SUR.
fuente
39

Puedes hacerlo:

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

o

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

Si desea verificar el archivo y la carpeta, use la -eopción en lugar de -f. -edevuelve verdadero para archivos regulares, directorios, socket, archivos especiales de caracteres, archivos especiales de bloque, etc.

Jahid
fuente
1
Eso no es específico de bash. La sintaxis proviene del shell Korn y también está disponible en zsh y bash. Sin [embargo, tiene una ventaja limitada sobre la utilidad estándar aquí.
Stephane Chazelas
los archivos normales (como los verificados -f) y los directorios son solo dos de muchos tipos diferentes de archivos . También hay sockets, enlaces simbólicos, dispositivos, fifos, puertas ... [ -eprobará la existencia del archivo (de cualquier tipo, incluido regular, fifo, directorio ...) después de la resolución del enlace simbólico .
Stephane Chazelas
@StephaneChazelas: No veo ninguna etiqueta ksh o zsh en ningún lado. Estamos hablando de bash o algo que está estandarizado en la mayoría de los sistemas unix. Entonces, dentro del contexto, es específico de bash. OP no necesita saber de todos los proyectiles: D
Jahid
Ese fue un comentario sobre la parte "Bash específica" de su respuesta.
Stephane Chazelas
@StephaneChazelas: Sí, y dentro del contexto (bash y el estándar sh), es específico de bash. No veo el punto en su segundo comentario, está totalmente fuera de contexto. OP no necesita -e, él necesita -f.
Jahid
35

Debe tener cuidado al ejecutar testuna variable sin comillas, ya que podría producir resultados inesperados:

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

La recomendación generalmente es tener la variable probada entre comillas dobles:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi
artdanil
fuente
8
La recomendación es tener todas las variables entre comillas dobles, a menos que sepa exactamente que tiene uno de los casos raros en los que es innecesario, o uno de los casos aún más raros en los que es dañino. (Y no, este no es uno de ellos.)
Uwe
¿Le gustaría explicar por qué este no es el caso para usar comillas dobles? De lo contrario, no veo la utilidad en el comentario.
artdanil
44
Quise decir: este no es uno de los raros casos en que es innecesario o dañino. Un programador de shell debería acostumbrarse a encerrar (casi) cada variable entre comillas dobles; Esta regla no se limita a [ ... ].
Uwe
24

Hay tres formas distintas de hacer esto:

  1. Niegue el estado de salida con bash (ninguna otra respuesta ha dicho esto):

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

    O:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Niegue la prueba dentro del comando de prueba [(esa es la forma en que la mayoría de las respuestas se han presentado antes):

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

    O:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Actúe sobre el resultado de que la prueba sea negativa (en ||lugar de &&):

    Solamente:

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

    Esto parece una tontería (IMO), no lo use a menos que su código tenga que ser portátil para el shell Bourne (como el /bin/shde Solaris 10 o anterior) que carecía del operador de negación de canalización ( !):

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi
Stephane Chazelas
fuente
¿Alguna diferencia de portabilidad entre ! [y [ !?
Llama congelada
3
El ! [ es POSIX para oleoductos de Shell 2.9.2 (cualquier comando) Otherwise, the exit status shall be the logical NOT of the exit status of the last command y [ ! es POSIX para la prueba ! expression True if expression is false. False if expression is true. lo tanto, ambos son POSIX, y en mi experiencia, ambos son compatibles ampliamente.
1
@FrozenFlame, el shell Bourne no tenía la !palabra clave introducida por el shell Korn. Excepto tal vez para Solaris 10 y versiones anteriores, es poco probable que encuentre un shell Bourne en estos días.
Stephane Chazelas
23

En

[ -f "$file" ]

el [comando realiza una stat()(no lstat()) llamada al sistema en la ruta almacenada $filey devuelve verdadero si esa llamada al sistema tiene éxito y el tipo de archivo que devuelve stat()es " regular ".

Entonces, si [ -f "$file" ]devuelve verdadero, puede decir que el archivo existe y que es un archivo normal o un enlace simbólico que finalmente se resuelve en un archivo normal (o al menos lo era en el momento de stat()).

Sin embargo si devuelve falso (o si [ ! -f "$file" ]o ! [ -f "$file" ]devolver true), hay muchas posibilidades diferentes:

  • el archivo no existe
  • el archivo existe pero no es un archivo normal (podría ser un dispositivo, quince, directorio, socket ...)
  • el archivo existe pero no tiene permiso de búsqueda para el directorio padre
  • el archivo existe pero esa ruta para acceder es demasiado larga
  • el archivo es un enlace simbólico a un archivo normal, pero no tiene permiso de búsqueda para algunos de los directorios involucrados en la resolución del enlace simbólico.
  • ... cualquier otra razón por la cual la stat()llamada al sistema puede fallar.

En resumen, debería ser:

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

Para saber con certeza que el archivo no existe, necesitaríamos que la stat()llamada del sistema regrese con un código de error de ENOENT( ENOTDIRnos dice que uno de los componentes de la ruta no es un directorio, es otro caso en el que podemos decir que el archivo no existir por ese camino). Desafortunadamente, el[ comando no nos deja saber eso. Devolverá falso si el código de error es ENOENT, EACCESS (permiso denegado), ENAMETOOLONG o cualquier otra cosa.

La [ -e "$file" ]prueba también se puede hacer con ls -Ld -- "$file" > /dev/null. En ese caso, lsle diremos por qué stat()falló, aunque la información no se puede usar fácilmente mediante programación:

$ 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

Al menos lsme dice que no es porque el archivo no existe que falla. Es porque no puede decir si el archivo existe o no. El [comando simplemente ignoró el problema.

Con el zshshell, puede consultar el código de error con la $ERRNOvariable especial después del [comando que falla y decodificar ese número utilizando la $errnosmatriz especial en el zsh/systemmódulo:

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

(Tenga en cuenta que el $errnossoporte se rompió con algunas versiones de zshcuando se creó con versiones recientes degcc ).

Stephane Chazelas
fuente
11

Para revertir una prueba, use "!". Eso es equivalente al operador lógico "no" en otros idiomas. Prueba esto:

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

O escrito de una manera ligeramente diferente:

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

O podrías usar:

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

O, presionando todos juntos:

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

Que puede escribirse (usando el operador "y": &&) como:

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

Que se ve más corto así:

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

fuente
10

Este código también funciona.

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

La forma mas simple

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

Este script de shell también funciona para encontrar un archivo en un directorio:

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
Simmant
fuente
3
No está claro que esto agregue algo que otras respuestas aún no hayan dado. Codifica un nombre de ruta. Dado que la pregunta está etiquetada como bash , podría usarse read -p "Enter file name: " -r apara preguntar y leer. Utiliza comillas alrededor de la variable; eso es bueno, pero debería explicarse. Podría ser mejor si se hizo eco del nombre del archivo. Y esto comprueba que el archivo existe y no está vacío (ese es el significado de -s), mientras que la pregunta se refiere a cualquier archivo, vacío o no (para el cual -fes más apropiado).
Jonathan Leffler
4

a veces puede ser útil usar && y || operadores.

Como en (si tiene el comando "prueba"):

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

o

test -b $FILE || echo File there!
André
fuente
2

Si desea usar en testlugar de [], puede usar !para obtener la negación:

if ! test "$FILE"; then
  echo "does not exist"
fi
Mauro Zallocco
fuente
0

También puede agrupar múltiples comandos en un solo revestimiento

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

o

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

Si el nombre de archivo no sale, la salida será

test1
test2
test3

Nota: (...) se ejecuta en un subshell, {...;} se ejecuta en el mismo shell. La notación de corchete funciona solo en bash.

upteryx
fuente
1
No, la notación de corchetes no es solo bash; Funciona en cualquier shell compatible con POSIX.
Charles Duffy el
0
envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi
kta
fuente
Proporcione una explicación junto con el código.
zohar hace
Si bien este código puede resolver el problema del OP, es mejor incluir una explicación sobre cómo su código aborda el problema del OP. De esta manera, los futuros visitantes pueden aprender de su publicación y aplicarla a su propio código. SO no es un servicio de codificación, sino un recurso para el conocimiento. Además, las respuestas completas de alta calidad tienen más probabilidades de ser votadas. Estas características, junto con el requisito de que todas las publicaciones sean independientes, son algunos de los puntos fuertes de SO como plataforma, que lo diferencia de los foros. Puede editar para agregar información adicional y / o complementar sus explicaciones con la documentación de origen.
ysf hace