Как мне узнать, что обычный файл не существует в Bash? - PullRequest
2938 голосов
/ 12 марта 2009

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

#!/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

Ответы [ 18 ]

19 голосов
/ 14 октября 2016

В

[ -f "$file" ]

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

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

Однако, если он возвращает false (или если [ ! -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).

10 голосов
/ 05 мая 2013

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

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!"
9 голосов
/ 03 июля 2015

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

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi
8 голосов
/ 31 марта 2013

test вещь тоже может иметь значение. Это сработало для меня (на основе Bash Shell: Проверить наличие файла или нет ):

test -e FILENAME && echo "File exists" || echo "File doesn't exist"
6 голосов
/ 21 мая 2014

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

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
5 голосов
/ 29 августа 2013

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

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 голосов
/ 11 апреля 2017

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

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

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

или

test -b $FILE || echo File there!
0 голосов
/ 12 июня 2019

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

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

или

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

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

test1
test2
test3

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

...