Как мне узнать, что обычный файл не существует в Bash?

3268

Я использовал следующий скрипт, чтобы увидеть, существует ли файл:

#!/bin/bash

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

Какой правильный синтаксис использовать, если я только хочу проверить, не существует ли файл ?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi
Билл Ящерица
источник
187
Я нашел этот список условных операторов bash очень полезным.
Саулюс Жемайтайтис
9
Будучи очень ленивым человеком, которым я являюсь, я обычно использовал бы следующую глупую обходную конструкцию: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Вероятно, хорошо, что вместо этого я нашел этот вопрос и научился делать его более правильным образом. :)
Alderath
5
Чтобы быть подвесным, вы должны сказать «обычный файл», так как большинство документов UNIX / POSIX обычно ссылаются на все типы записей файловой системы просто на «файлы», например, символическая ссылка - это тип файла, как и именованный канал , обычный файл, каталог, специальный блок, специальный символ, сокет и т. д.
kevinarpe
11
@kevinarpe, если вы хотите проверить, существует ли что-то , используйте -e. -f не будет забирать каталоги, символические
ссылки
14
Чтобы быть в безопасности, всегда используйте двойные кавычки для правильной обработки имен файлов с пробелами, например, FILE=$1-> FILE="$1"и if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Ответы:

4528

Команда test ( [здесь) имеет «не» логический оператор, который является восклицательным знаком (аналогично многим другим языкам). Попробуй это:

if [ ! -f /tmp/foo.txt ]; then
    echo "File not found!"
fi
Джон Феминелла
источник
208
Более кратко: [! -f /tmp/foo.txt] && echo "Файл не найден!"
DavidWinterbottom
38
Я изо всех сил пытался найти правильный синтаксис для «если какой-либо из 2 файлов не существует». Следующие обе работы:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
MIVK
153
@DavidWinterbottom Еще более сочно:[ -f /tmp/foo.txt ] || echo "File not found!"
Дэвид В.
27
Параметр может быть любым из следующих:-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
Существует асимметрия в использовании ! -fс по &&сравнению с использованием -fс ||. Это связано с кодом выхода, возвращаемым проверкой отсутствия / существования. Если вам нужно, чтобы ваша строка всегда корректно выходила с кодом выхода 0 (а иногда вам не нужно это ограничение), эти два подхода не являются взаимозаменяемыми. В качестве альтернативы, просто используйте ifоператор, и вам больше не нужно беспокоиться о коде завершения вашей проверки не / существования.
Acumenus
670

Тестирование файлов Bash

-b filename- Блокировать специальный файл
-c filename- Файл специальных символов
-d directoryname- Проверить наличие каталога
-e filename- Проверить существование файла, независимо от его типа (узел, каталог, сокет и т. Д.)
-f filename- Проверить наличие файла в обычном режиме, а не каталог
-G filename- Проверить, существует ли файл и принадлежит ли он эффективный идентификатор группы
-G filename set-group-id- Истинно, если файл существует и задан идентификатором группы
-k filename- Закрепленный бит
-L filename- Символическая ссылка
-O filename- Истинно, если файл существует и принадлежит эффективному идентификатору пользователя
-r filename- Проверить, доступен ли файл для чтения
-S filename- Проверить, является ли файл сокетом
-s filename- Проверить размер файла не равен нулю
-u filename- проверьте, установлен ли бит set-user-id
-w filenameфайла
-x filename- проверьте, доступен ли файл для записи - проверьте, является ли файл исполняемым

Как пользоваться:

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

Проверяемое выражение может быть сведено на нет с помощью !оператора

#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
    echo "File does not exist"
else 
    echo "File exists"
fi 
BlueCacti
источник
1
@ 0x90 Если хочешь, можешь свободно редактировать мой пост и добавлять его в список. Я предполагаю, что вы имеете в виду: -n String- Проверьте, не равна ли длина строки нулю. Или вы имеете в виду file1 -nt file2- Проверьте, является ли file1 более новым, чем файл 2 (вы также можете использовать -ot для более старшего)
BlueCacti
1
О -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
почему некоторые не добавили функцию, подобную функции Существует () {⏎ if [-e "$ 1"]; тогда эхо "$ 1 существует", иначе эхо "$ 1 не существует" fi}
Mz A
291

Вы можете отменить выражение с помощью "!":

#!/bin/bash
FILE=$1

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

Соответствующей страницей руководства является man testили, что эквивалентно, man [- или help testили help [для встроенной команды bash.

starbeamrainbowlabs
источник
4
В Bash , [это встроенный. Таким образом, соответствующая информация скорее получается help [... но это показывает, что [это синоним testвстроенного, следовательно, соответствующая информация скорее получается help test. См. Также раздел «Условные выражения Bash» в руководстве .
gniourf_gniourf
@gniourf_gniourf: Да, но встроенная [команда bash ведет себя очень похоже на внешнюю [команду, так что либо она, man testлибо man [даст вам хорошее представление о том, как она работает.
Кит Томпсон
8
@KeithThompson за исключением того, что встроенная команда bash[ имеет больше переключателей, чем внешняя команда, [найденная в моей системе ... Вообще говоря, я считаю, что лучше читать документацию, относящуюся к конкретному инструменту, а не документацию, относящуюся к другому неопределенно связанному. Хотя я могу ошибаться;)
gniourf_gniourf
134
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

Также возможно, что файл является неработающей символической ссылкой или нестандартным файлом, например, сокетом, устройством или fifo. Например, чтобы добавить проверку на битые символические ссылки:

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
Могу ли я спросить, почему два "[" в тесте? (например, [[! -a $ FILE]]). Я перепробовал все варианты, упомянутые на коробке Solaris, и только тот, который работал, так благодарен, но почему?
Димитриос Мистриотис
30
Двойные скобки являются «современным» расширением; например, они не будут разбивать
bw1024
7
в соответствии с tldp.org/LDP/abs/html/fto.html -a фактически идентичен -e. Это было "устарело", и его использование не рекомендуется. Во всяком случае +1 за упоминание, чтобы проверить на сломанную символическую ссылку тоже
Лука Боррионе
4
@dimitrismistriotis two "[" - это непереносимое расширение, реализованное (иначе) zsh & bash; как правило, вы должны избегать этого, если это вообще возможно.
Хороший человек
7
Я проголосовал за это, потому что он использовал [[. Это широко используемое расширение. Если вы знаете, что используете bash, то нет причин не использовать его. Это гораздо менее подвержен ошибкам, чем [.
Майкл Поттер
101

Стоит отметить, что если вам нужно выполнить одну команду, вы можете сократить

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

в

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

или

[ -f "$file" ] || echo "$file"
Элазар Лейбович
источник
Спасибо! Нужна альтернатива, которая не использует [[]]
Джонатан
69

Я предпочитаю делать следующий однострочник в формате, совместимом с POSIX :

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

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

Для пары команд, как я сделал бы в сценарии:

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

Как только я начал это делать, я уже редко использую полностью типизированный синтаксис !!

Дж. М. Беккер
источник
1
Прежде всего, ссылки на переменные без кавычек подвержены ошибкам. Тем не менее, где говорится в любой man-странице bash, что встроенная [или testбудет проверять наличие аргумента по умолчанию в файле (в отличие от -e)? Разве это не было бы двусмысленным? AFAIK (и AIUI в разделе «УСЛОВНЫЕ ВЫРАЖЕНИЯ») единственное, что проверяется вашим подходом, это то, что аргумент не является пустым (или неопределенным), что в данном случае является тавтологией (пусть $DIR = ''и $FILE = '', тогда аргумент все еще '//').
PointedEars
1
Доказательство:, ls /fooрезультат ls: cannot access /foo: No such file or directory. [ /foo ] && echo 42, результат 42. GNU bash, версия 4.2.37 (1) -релиз (i486-pc-linux-gnu).
PointedEars
@PointedEars: мне не удалось указать -fопцию, в данный момент я написал этот ответ. Очевидно, вы всегда можете использовать -e, если вы не уверены, что это будет обычный файл. Кроме того, во всех моих сценариях я цитирую эти конструкции, я, должно быть, только что представил это без адекватного подтверждения.
Дж. М. Беккер
ACK. Но вы, вероятно, знаете, что одна строка не может решить проблему if-else: [ $condition ] && if_true || if_falseподвержена ошибкам. В любом случае мне [ ! -f "$file" ] && if_not_existsлегче читать и понимать, чем [ -f "$file" ] || if_not_exists.
PointedEars
55

Чтобы проверить существование файла, параметр может быть любым из следующих:

-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

Все приведенные ниже тесты применяются к обычным файлам, каталогам и символическим ссылкам:

-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

Пример скрипта:

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi
SD.
источник
39

Ты можешь сделать это:

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

или

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

Если вы хотите проверить оба файла и папки, используйте -eпараметр вместо -f. -eвозвращает true для обычных файлов, каталогов, сокетов, специальных файлов символов, блокировки специальных файлов и т. д.

Jahid
источник
1
Это не специфично для bash. Синтаксис взят из оболочки Korn и также доступен в zsh и bash. Здесь у него ограниченное преимущество перед стандартной [утилитой.
Стефан Шазелас
обычные файлы (как проверено -f) и каталоги - это только два из множества различных типов файлов . Существуют также сокеты, символические ссылки, устройства, fifos, doors ..., которые [ -eбудут проверять наличие файлов (любого типа, включая обычный, fifo, directory ...) после разрешения символической ссылки .
Стефан Шазелас
@StephaneChazelas: я нигде не вижу тегов ksh или zsh. Мы говорим о bash или о чем-то, что стандартизировано в большинстве систем Unix. Так что, в контексте, это специфично для Bash. ОП не нужно знать обо всех снарядах: D
Джахид
Это был комментарий к «специфичной для Bash» части вашего ответа.
Стефан Шазелас
@StephaneChazelas: Да, и в контексте (bash и стандартная sh), это зависит от bash. Я не вижу смысла в вашем втором комментарии, это совершенно вне контекста. ОП не нужен -e, он нужен -f.
Джахид
35

Вы должны быть осторожны при запуске testпеременной без кавычек, потому что это может привести к неожиданным результатам:

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

Обычно рекомендуется, чтобы проверяемая переменная была заключена в двойные кавычки:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi
artdanil
источник
8
Рекомендуется заключать каждую переменную в двойные кавычки, если только вы точно не знаете, что у вас есть один из редких случаев, когда это не нужно, или один из еще более редких случаев, когда это вредно. (И нет, это не один из них.)
Уве
Не могли бы вы уточнить, почему нельзя использовать двойные кавычки? В противном случае я не вижу полезности в комментарии.
artdanil
4
Я имел в виду: это не один из тех редких случаев, когда это ненужно или вредно. Программист оболочки должен привыкнуть заключать (почти) каждую переменную в двойные кавычки; это правило не ограничивается [ ... ].
Уве
См. Также stackoverflow.com/questions/10067266/…
tripleee
24

Есть три различных способа сделать это:

  1. Отмените статус выхода с помощью bash (другой ответ не сказал этого):

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

    Или:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Отмените тест внутри команды test [(именно так было представлено большинство ответов):

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

    Или:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Действуйте, чтобы результат теста был отрицательным ( ||вместо &&):

    Только:

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

    Это выглядит глупо (IMO), не используйте его, если ваш код не должен быть переносимым на оболочку Bourne (как в /bin/shSolaris 10 или более ранней версии), в которой отсутствует оператор отрицания конвейера ( !):

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi
Стефан Шазелас
источник
Есть ли разница в переносимости ! [и [ !?
Замороженное Пламя
3
Это ! [ POSIX для конвейеров оболочки 2.9.2 (любая команда) Otherwise, the exit status shall be the logical NOT of the exit status of the last command и [ ! POSIX для тестирования. ! expression True if expression is false. False if expression is true. Итак, оба являются POSIX, и, по моему опыту, оба широко поддерживаются.
1
@FrozenFlame, оболочка Bourne не имела !ключевого слова, которое было введено оболочкой Korn. За исключением, возможно, Solaris 10 и старше, вы вряд ли столкнетесь с оболочкой Bourne в эти дни.
Стефан Шазелас
23

В

[ -f "$file" ]

[команда делает stat()(не lstat()) системный вызов на пути , хранящегося в$file и возвращает истинное если системный вызов завершается успешно и тип файла, возвращаемый stat()является « регулярным ».

Таким образом, если [ -f "$file" ]возвращает true, вы можете сказать, что файл существует и является обычным файлом или символической ссылкой, в конечном итоге преобразующейся в обычный файл (или, по крайней мере, это было во время stat()).

Однако, если он вернется false (или if [ ! -f "$file" ]или ! [ -f "$file" ]возвращает true), существует много разных возможностей:

  • файл не существует
  • файл существует, но не является обычным файлом (это может быть устройство, fifo, каталог, сокет ...)
  • файл существует, но у вас нет разрешения на поиск в родительском каталоге
  • файл существует, но путь к нему слишком длинный
  • файл является символической ссылкой на обычный файл, но у вас нет разрешения на поиск в некоторых каталогах, участвующих в разрешении символической ссылки.
  • ... любая другая причина, по которой stat()системный вызов может потерпеть неудачу.

Короче, это должно быть:

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

Чтобы точно знать, что файл не существует, нам потребуется stat()системный вызов для возврата с кодом ошибки ENOENT( ENOTDIRсообщает нам, что один из компонентов пути не является каталогом, это другой случай, когда мы можем сказать, что файл не существует существовать по этому пути). К сожалению, [команда не дает нам знать это. Он вернет false, независимо от того, является ли код ошибки ENOENT, EACCESS (разрешение отклонено), ENAMETOOLONG или что-то еще.

[ -e "$file" ]Тест также может быть сделано с ls -Ld -- "$file" > /dev/null. В этом случае lsвам расскажут, почему произошел stat()сбой, хотя информацию нельзя легко использовать программно:

$ 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

По крайней мере, lsговорит мне, что это не потому, что файл не существует, что он терпит неудачу. Это потому, что он не может сказать, существует файл или нет. Команда [просто проигнорировала проблему.

С помощью zshоболочки вы можете запросить код ошибки с помощью $ERRNOспециальной переменной после неудачной [команды и декодировать это число, используя $errnosспециальный массив в zsh/systemмодуле:

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

(имейте в виду, что $errnosподдержка была нарушена в некоторых версиях, zshкогда они были собраны с последними версиямиgcc ).

Стефан Шазелас
источник
11

Чтобы отменить тест, используйте «!». Это эквивалентно «не» логическому оператору в других языках. Попробуй это:

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

Или написано немного по-другому:

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

Или вы можете использовать:

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

Или, давя все вместе:

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

Который может быть записан (используя оператор «и»: &&) как:

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

Который выглядит короче, как это:

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

источник
10

Этот код также работает.

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi
Лимонный Кази
источник
7

Самый простой способ

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
lancerex
источник
6

Этот сценарий оболочки также работает для поиска файла в каталоге:

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
источник
3
Не ясно, добавляет ли это что-то, что другие ответы еще не дали. Он жестко кодирует путь. Поскольку вопрос помечен как bash , его можно использовать как read -p "Enter file name: " -r aдля подсказки, так и для чтения. Он использует кавычки вокруг переменной; это хорошо, но должно быть объяснено. Возможно, было бы лучше, если бы оно повторяло имя файла. И это проверяет, что файл существует и не является пустым (в этом смысл -s), тогда как вопрос касается любого файла, пустого или нет (для которого -fболее уместно).
Джонатан Леффлер
4

иногда может быть удобно использовать && и || операторы.

Как в (если у вас есть команда «тест»):

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

или

test -b $FILE || echo File there!
Андре
источник
2

Если вы хотите использовать testвместо [], то вы можете использовать, !чтобы получить отрицание:

if ! test "$FILE"; then
  echo "does not exist"
fi
Мауро Заллокко
источник
0

Вы также можете сгруппировать несколько команд в один вкладыш

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

или

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

Если имя файла не выходит, вывод будет

test1
test2
test3

Примечание: (...) работает в подоболочке, {...;} работает в той же оболочке. Запись в фигурных скобках работает только в bash.

upteryx
источник
1
Нет, запись в фигурных скобках не только для bash; он работает в любой POSIX-совместимой оболочке.
Чарльз Даффи
0
envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi
КТА
источник
Пожалуйста, предоставьте объяснение вместе с кодом.
Зохар
Хотя этот код может решить проблему OP, лучше всего включить объяснение того, как ваш код решает проблему OP. Таким образом, будущие посетители могут учиться на вашем посте и применять его к своему коду. SO не сервис кодирования, а ресурс для знаний. Кроме того, высокое качество, полные ответы, скорее всего, будет проголосовано. Эти функции, наряду с требованием, чтобы все сообщения были автономными, являются одними из сильных сторон SO как платформы, которая отличает ее от форумов. Вы можете редактировать, чтобы добавить дополнительную информацию и / или дополнить ваши пояснения исходной документацией.
YSF