Como posso saber se um arquivo regular não existe no Bash?


3266

Usei o seguinte script para verificar se existe um arquivo:

#!/bin/bash

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

Qual é a sintaxe correta a ser usada se eu quiser apenas verificar se o arquivo não existe?

#!/bin/bash

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


9
Sendo a pessoa muito preguiçosa que sou, normalmente usaria a seguinte solução alternativa boba: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Provavelmente bom que eu encontrei essa pergunta e aprendi a fazê-la de uma maneira mais adequada. :)
— Alderath

5
Para ser pendente, você deve dizer "arquivo regular", pois a maioria dos documentos do UNIX / POSIX se refere genericamente a todos os tipos de entradas do sistema de arquivos e simplesmente a "arquivos", por exemplo, um link simbólico é um tipo de arquivo, assim como um pipe nomeado , arquivo regular, diretório, bloco especial, especial caráter, socket, etc.
— kevinarpe

11
@kevinarpe se você quiser testar se algo existe, use -e. -f não vai pegar diretórios, links simbólicos, etc.
— Benubird

14
Para ser seguro, sempre usar aspas duplas para corretamente os nomes de arquivo identificador com um espaço em branco, por exemplo, FILE=$1-> FILE="$1"e if [ -f $FILE ];->if [ -f "$FILE" ];
— kevinarpe

Respostas:


4526

O comando test ( [aqui) possui um operador lógico "não", que é o ponto de exclamação (semelhante a muitos outros idiomas). Tente o seguinte:

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

208
Mais sucintamente: [! -f /tmp/foo.txt] && echo "Arquivo não encontrado!"
— DavidWinterbottom 29/09

38
Eu lutei um pouco para encontrar a sintaxe certa para "se algum dos 2 arquivos não existir". O seguinte funciona:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
— mivk

153
@DavidWinterbottom Ainda mais succulently:[ -f /tmp/foo.txt ] || echo "File not found!"
— David W.

27
O parâmetro pode ser qualquer um dos seguintes:-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
Existe uma assimetria no uso ! -fcom &&versus usando -fcom ||. Isso tem a ver com o código de saída retornado pela verificação de inexistência. Se você precisa que sua linha sempre saia limpa com o código de saída 0 (e às vezes você não deseja essa restrição), as duas abordagens não são intercambiáveis. Como alternativa, basta usar uma ifinstrução e você não precisa mais se preocupar com o código de saída da sua verificação de não existência.
— Acumenus 01/08/15

670

Teste de arquivo bash

-b filename- Bloquear arquivo especial
-c filename- Arquivo de caractere especial
-d directoryname- Verificar a existência do diretório
-e filename- Verificar a existência do arquivo, independentemente do tipo (nó, diretório, soquete etc.)
-f filename- Verificar a existência regular do arquivo e não um diretório
-G filename- Verificar se o arquivo existe e é de propriedade de ID do grupo efetivo
-G filename set-group-id- Verdadeiro se o arquivo existir e estiver definido como id do grupo
-k filename- Bit fixo
-L filename- Link simbólico
-O filename- Verdadeiro se o arquivo existir e pertencer ao ID do usuário efetivo
-r filename- Verifique se o arquivo é legível
-S filename- Verifique se o arquivo é soquete
-s filename- Verifique se arquivo com tamanho diferente de zero
-u filename- Verifique se o bit do conjunto de IDs de usuário está definido
-w filename- Verifique se o arquivo é gravável
-x filename- Verifique se o arquivo é executável

Como usar:

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

Uma expressão de teste pode ser negada usando o !operador

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

1
@ 0x90 Se quiser, você pode editar minha postagem e adicioná-la à lista. Eu acho que você quer dizer: -n String- Verifique se o comprimento da string não é zero. Ou você quer dizer file1 -nt file2- Verifique se o arquivo1 é mais recente, em seguida, apresentar 2 (você também pode usar -ot para idosos seguida)
— BlueCacti

1
Sobre -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 que alguns não adicionaram uma função como a função existe () {⏎ se [-e "$ 1"]; então echo "$ 1 existe" senão echo "$ 1 não existe" fi}
— Mz A

291

Você pode negar uma expressão com "!":

#!/bin/bash
FILE=$1

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

A página de manual relevante é man testou, equivalentemente, man [- ou help testou help [para o comando bash interno.


4
No bash , [é um builtin. Portanto, as informações relevantes são obtidas por help [... mas isso mostra que [é um sinônimo para o testbuiltin; portanto, as informações relevantes são obtidas por help test. Consulte também a seção Expressão condicional do bash no manual .
— gniourf_gniourf

@gniourf_gniourf: Sim, mas o [comando interno do bash se comporta de maneira muito semelhante ao [comando externo , portanto, man testou man [fornecerá uma boa idéia de como ele funciona.
— 21413 Keith Thompson

8
@ KeithThompson, exceto que o bash builtin [possui mais opções do que o comando externo [encontrado no meu sistema ... De um modo geral, acredito que é melhor ler a documentação específica de uma determinada ferramenta, e não a documentação específica de outra vagamente relacionada. Eu posso estar errado, embora;)
— gniourf_gniourf

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

Além disso, é possível que o arquivo seja um link simbólico quebrado ou um arquivo não regular, como, por exemplo, um soquete, dispositivo ou fifo. Por exemplo, para adicionar uma verificação de links simbólicos quebrados:

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 perguntar por que os dois "[" estão no teste? (por exemplo, [[! -a $ FILE]]). Eu tentei todas as opções mencionadas em uma caixa solaris e somente essa funcionou, muito grata, mas por quê?
— Dimitrios Mistriotis

30
Parênteses duplos são uma extensão "moderna"; por exemplo, eles não dividem palavras (como nomes de arquivos com espaços) e ainda funcionam para cadeias de caracteres vazias: mywiki.wooledge.org/BashFAQ/031
— bw1024

7
de acordo com tldp.org/LDP/abs/html/fto.html -a é idêntico em efeito a -e. Foi "preterido" e seu uso é desencorajado. De qualquer modo 1 para mencionar para verificar symlink quebrado também
— Luca Borrione

4
@dimitrismistriotis two "[" é uma extensão não portátil implementada (de forma diferente) pelo zsh & bash; geralmente você deve evitá-lo, se possível.
— Boa Pessoa

7
Eu votei isso porque usava [[. É uma extensão amplamente usada. Se você sabe que está usando o bash, não há motivo para não usá-lo. É muito menos propenso a erros do que [.
— Michael Potter

101

Vale ressaltar que, se você precisar executar um único comando, poderá abreviar

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

para

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

ou

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

Obrigado! Precisava de uma alternativa que não usasse [[]] #
— 315 Jonathan Jonathan

69

Prefiro fazer o seguinte one-liner, no formato compatível com shell POSIX :

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

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

Para alguns comandos, como eu faria em um script:

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

Depois que comecei a fazer isso, raramente uso mais a sintaxe totalmente digitada !!


1
Primeiro de tudo, as referências de variáveis ​​não citadas são propensas a erros. Dito isto, onde diz em qualquer página de manual do bash que o [ou testembutido testaria a existência de arquivo do argumento por padrão (em oposição a -e)? Isso não seria ambíguo? AFAIK (e AIUI a seção "EXPRESSÕES CONDICIONAIS"), a única coisa testada com sua abordagem é que o argumento não está vazio (ou indefinido), que é, nesse caso, uma tautologia (deixe $DIR = ''e $FILE = '', então o argumento ainda está '//')
— PointedEars

1
Prova:, ls /fooresultado ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, resultado 42. Lançamento do GNU bash, versão 4.2.37 (1) (i486-pc-linux-gnu).
— PointedEars

@PointedEars: não especifiquei a -fopção, no momento em que escrevi esta resposta. Obviamente, você sempre pode usar -e, se não tiver certeza, será um arquivo comum. Além disso, em todos os meus scripts, cito essas construções, devo ter acabado de submetê-las sem provas adequadas.
— JM Becker

ACK. Mas você provavelmente sabe que uma linha não pode resolver o problema do caso contrário: [ $condition ] && if_true || if_falseé propenso a erros. De qualquer forma, acho [ ! -f "$file" ] && if_not_existsmais fácil ler e entender do que [ -f "$file" ] || if_not_exists.
— PointedEars 18/11/12

55

Para testar a existência do arquivo, o parâmetro pode ser qualquer um dos seguintes:

-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

Todos os testes abaixo se aplicam a arquivos, diretórios e links simbólicos regulares:

-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 exemplo:

#!/bin/bash
FILE=$1

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

39

Você consegue fazer isso:

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

ou

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

Se você deseja procurar arquivos e pastas, use a -eopção em vez de -f. -eretorna true para arquivos regulares, diretórios, soquete, arquivos especiais de caracteres, bloquear arquivos especiais etc.


1
Isso não é específico do bash. A sintaxe vem do shell Korn e também está disponível no zsh e no bash. [Porém, ele tem uma vantagem limitada sobre o utilitário padrão .
— Stephane Chazelas

arquivos regulares (conforme verificado por -f) e diretórios são apenas dois dos muitos tipos diferentes de arquivos . Também existem soquetes, links simbólicos, dispositivos, fifos, portas ... [ -etestará a existência de arquivos (de qualquer tipo, incluindo diretório regular, fifo, ...) após a resolução do link simbólico .
— Stephane Chazelas

@StephaneChazelas: Não vejo nenhuma tag ksh ou zsh em lugar algum. Estamos falando de bash ou algo padronizado na maioria dos sistemas unix. Portanto, dentro do contexto, é específico do bash. O OP não precisa saber de todas as conchas existentes: D
— Jahid

Esse foi um comentário sobre o "específico do Bash" da sua resposta.
— Stephane Chazelas

@StephaneChazelas: Sim, e dentro do contexto (bash e sh padrão), é específico do bash. Não vejo o ponto no seu segundo comentário, está totalmente fora de contexto. OP não precisa-e , ele precisa -f.
— Jahid

35

Você deve ter cuidado ao executar testuma variável não citada, pois ela pode produzir resultados inesperados:

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

A recomendação é geralmente ter a variável testada entre aspas duplas:

#!/bin/sh
FILE=$1

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

8
A recomendação é ter todas as variáveis ​​entre aspas duplas, a menos que você saiba exatamente que possui um dos raros casos em que é desnecessário ou um dos casos ainda mais raros em que é prejudicial. (E não, este não é um deles.) #
— 315 Uwe

Você gostaria de explicar por que esse não é o caso de usar aspas duplas? Caso contrário, não vejo a utilidade no comentário.
— Artdanil 28/05

4
Eu quis dizer: este não é um dos raros casos em que é desnecessário ou prejudicial. Um programador de shell deve se acostumar a incluir (quase) todas as variáveis ​​entre aspas duplas; Esta regra não está limitada a [ ... ].
— Uwe


24

Existem três maneiras distintas de fazer isso:

  1. Negue o status de saída com bash (nenhuma outra resposta disse isso):

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

    Ou:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Negue o teste dentro do comando test [(essa é a maneira que a maioria das respostas apresentou anteriormente):

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

    Ou:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Aja com o resultado negativo do teste (em ||vez de&& ):

    Somente:

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

    Isso parece bobo (IMO), não o use, a menos que seu código precise ser portátil para o shell Bourne (como o /bin/shSolaris 10 ou anterior) que não possui o operador de negação de pipeline ( !):

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

Alguma diferença de portabilidade entre ! [e [ !?
— Frozen Flame

3
O ! [ POSIX é para pipelines shell 2.9.2 (qualquer comando) Otherwise, the exit status shall be the logical NOT of the exit status of the last command e [ ! é POSIX para teste. ! expression True if expression is false. False if expression is true. Portanto, ambos são POSIX e, na minha experiência, ambos são amplamente suportados.

1
@FrozenFlame, o shell Bourne não tinha a !palavra - chave que foi introduzida pelo shell Korn. Exceto, talvez, pelo Solaris 10 e mais antigo, é improvável que você encontre um shell Bourne atualmente.
— Stephane Chazelas

23

No

[ -f "$file" ]

o [comando faz uma stat()(não lstat()) chamada do sistema no caminho armazenado $filee retorna true se a chamada do sistema for bem-sucedida e o tipo de arquivo retornado por stat()" regular ".

Portanto, se [ -f "$file" ]retorna true, você pode dizer que o arquivo existe e é um arquivo regular ou um link simbólico que eventualmente está sendo resolvido para um arquivo regular (ou pelo menos era no momento do stat()).

No entanto, se retornar falso (ou se [ ! -f "$file" ]ou ! [ -f "$file" ]retornar verdadeiro), há muitas possibilidades diferentes:

  • o arquivo não existe
  • o arquivo existe, mas não é regular (pode ser um dispositivo, fifo, diretório, soquete ...)
  • o arquivo existe, mas você não tem permissão de pesquisa para o diretório pai
  • o arquivo existe, mas esse caminho para acessá-lo é muito longo
  • o arquivo é um link simbólico para um arquivo normal, mas você não tem permissão de pesquisa para alguns dos diretórios envolvidos na resolução do link simbólico.
  • ... qualquer outro motivo pelo qual a stat()chamada do sistema possa falhar.

Em suma, deve 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 ter certeza de que o arquivo não existe, precisaríamos que a stat()chamada do sistema retornasse com um código de erro de ENOENT( ENOTDIRnos diz que um dos componentes do caminho não é um diretório, outro caso em que podemos dizer que o arquivo não existe) existir por esse caminho). Infelizmente, o [comando não nos informa disso. Ele retornará false se o código de erro é ENOENT, EACCESS (permissão negada), ENAMETOOLONG ou qualquer outra coisa.

O [ -e "$file" ]teste também pode ser feito com ls -Ld -- "$file" > /dev/null. Nesse caso, lsinformará o motivo da stat()falha, embora as informações não possam ser facilmente usadas programaticamente:

$ 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

Pelo menos lsme diz que não é porque o arquivo não existe que falha. É porque ele não pode dizer se o arquivo existe ou não. O [comando apenas ignorou o problema.

Com o zshshell, você pode consultar o código de erro com a $ERRNOvariável especial após o [comando com falha e decodificar esse número usando a $errnosmatriz especial no 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

(cuidado, o $errnossuporte foi quebrado com algumas versões de zshquando construído com versões recentes dogcc ).


11

Para reverter um teste, use "!". Isso é equivalente ao operador lógico "não" em outros idiomas. Tente o seguinte:

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

Ou escrito de uma maneira um pouco diferente:

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

Ou você pode usar:

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

Ou, apresentando todos juntos:

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

Que pode ser escrito (usando o operador "and": &&) como:

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

Que parece mais curto assim:

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


10

Este código também está funcionando.

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

7

A maneira mais simples

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

6

Esse script de shell também funciona para encontrar um arquivo em um diretório:

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
Não está claro que isso adicione algo que outras respostas ainda não tenham dado. Ele codifica um nome de caminho. Como a pergunta está marcada como bash , ela pode ser usada read -p "Enter file name: " -r apara solicitar e ler. Ele usa aspas em torno da variável; isso é bom, mas deve ser explicado. Seria melhor se ecoasse o nome do arquivo. E isso verifica se o arquivo existe e não está vazio (esse é o significado de -s), enquanto a pergunta pergunta sobre qualquer arquivo, vazio ou não (para o que -fé mais apropriado).
— Jonathan Leffler

4

às vezes pode ser útil usar && e || operadores.

Como em (se você tiver o comando "test"):

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

ou

test -b $FILE || echo File there!

2

Se você deseja usar em testvez de [], pode usar !para obter a negação:

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

0

Você também pode agrupar vários comandos em uma linha

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

ou

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

Se o nome do arquivo não sair, a saída será

test1
test2
test3

Nota: (...) é executado em um subshell, {...;} é executado no mesmo shell. A notação de colchete funciona somente no bash.


1
Não, a notação de colchete não é apenas para bash; funciona em qualquer shell compatível com POSIX.
— Charles Duffy #

0
envfile=.env

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

Forneça uma explicação junto com o código.
— zohar

Embora esse código possa resolver o problema do OP, é melhor incluir uma explicação sobre como o seu código soluciona o problema do OP. Dessa forma, futuros visitantes podem aprender com sua postagem e aplicá-la ao próprio código. O SO não é um serviço de codificação, mas um recurso para o conhecimento. Além disso, respostas completas de alta qualidade têm mais probabilidade de serem votadas. Esses recursos, juntamente com o requisito de que todas as postagens sejam independentes, são alguns dos pontos fortes do SO como plataforma, que o diferencia dos fóruns. Você pode editar para adicionar informações adicionais e / ou complementar suas explicações com a documentação de origem.
— ysf
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.