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
Bill the Lizard
fonte
187
Eu achei esta lista de instruções condicionais do bash muito útil.
Saulius Žemaitaitis 12/03/09
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
John Feminella
fonte
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 
BlueCacti
fonte
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.

starbeamrainbowlabs
fonte
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
armas
fonte
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"
Elazar Leibovich
fonte
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 !!

JM Becker
fonte
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
SD.
fonte
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.

Jahid
fonte
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
artdanil
fonte
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
Stephane Chazelas
fonte
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 ).

Stephane Chazelas
fonte
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!"

fonte
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
Lemon Kazi
fonte
7

A maneira mais simples

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
lancerex
fonte
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
Simmant
fonte
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!
André
fonte
2

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

if ! test "$FILE"; then
  echo "does not exist"
fi
Mauro Zallocco
fonte
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.

upteryx
fonte
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
kta
fonte
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