Як дізнатись, якщо звичайний файл не існує в Bash?


3260

Я використовував такий сценарій, щоб перевірити, чи існує файл:

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


9
Будучи дуже ледачою людиною, якою я є, я, як правило, використовував такий нерозумний конструктивний шлях: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Напевно, добре, що я знайшов це питання замість цього і навчився робити це більш правильним чином. :)
Alderath

5
Щоб бути підвісною, слід сказати "звичайний файл", оскільки більшість документів UNIX / POSIX загалом посилаються на всі типи записів файлової системи просто "файли", наприклад, символічне посилання - це тип файлу, як і названа труба , звичайний файл, каталог, спеціальний блок, символ special, socket тощо
kevinarpe

11
@kevinarpe, якщо ви хочете перевірити, чи щось існує, використовуйте -e. -Я не збиратимуть каталогів, посилань тощо.
Benubird

14
Щоб бути безпечним, завжди використовуйте подвійні лапки, щоб правильно обробляти імена файлів з пробілом, наприклад, FILE=$1-> FILE="$1"і if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Відповіді:


4520

Команда тесту ( [тут) має логічний оператор "не", який є знаком оклику (подібно до багатьох інших мов). Спробуйте це:

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!"
David W.

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- Істинно, якщо файл існує та встановлено-group-id
-k filename- Клейкий біт
-L filename- Символічне посилання
-O filename- Істинно, якщо файл існує та належить ефективний ідентифікатор користувача
-r filename- Перевірте, чи файл читабельний
-S filename- Перевірте, чи файл сокет
-s filename- Перевірте, чи Файл має ненульовий розмір
-u filename- Перевірте, чи встановлено біт набору користувача-ідентифікатора користувача
-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 

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
чому деякі не додали таку функцію, як функція існує () {⏎ якщо [-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.


4
У bash , [це вбудований. Таким чином, відповідна інформація скоріше отримується help [… але це показує, що [це синонім testвбудованого, отже, відповідну інформацію, скоріше, отримує help test. Дивіться також розділ Bash Conditional Expression у посібнику .
gniourf_gniourf

@gniourf_gniourf: Так, але вбудована [команда bash поводиться дуже схоже на зовнішню [команду, тому або man testабо man [дасть вам гарне уявлення про те, як вона працює.
Кіт Томпсон

8
@KeithThompson за винятком того, що bash вбудований [має більше комутаторів, ніж зовнішня команда, [знайдена в моїй системі ... Взагалі кажучи, я вважаю, що краще читати документацію, специфічну для певного інструменту, а не документацію, специфічну для іншої неясно пов'язаної. Я, можливо, помиляюся;)
gniourf_gniourf

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

Крім того, можливо, що файл є розірваним символьним посиланням або нерегулярним файлом, наприклад, сокетом, пристроєм або фіфо. Наприклад, щоб додати чек на зламані символьні посилання:

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
Подвійні дужки - це "сучасне" розширення; наприклад, вони не робитимуть розділення слів (наприклад, для імен файлів з пробілами) і все ще працюють для порожніх рядків: mywiki.wooledge.org/BashFAQ/031
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
Перш за все, не цитуються посилання на змінну є схильними до помилок. Це означає, де в будь-якій програмі bash написано, що [або testвбудований тестує наявність файлу аргументу за замовчуванням (на відміну від -e)? Це не було б неоднозначно? AFAIK (і AIUI розділ "УМОВНІ ВИРАЗИ"), єдине, що перевіряється при вашому підході, - це те, що аргумент не є порожнім (або невизначеним), що в даному випадку є тавтологією (нехай $DIR = ''і $FILE = '', тоді аргумент все-таки є '//').
Вказано вуха

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: Не вдалося вказати -fваріант, на даний момент я написав цю відповідь. Очевидно, ви завжди можете користуватися -e, якщо не впевнені, що це буде звичайний файл. Крім того, у всіх своїх сценаріях я цитую ці конструкції, я, мабуть, щойно подав це без належного підтвердження.
Дж. М. Бекер

ACK. Але ви, мабуть, знаєте, що однолінійний не може вирішити проблему if-else: [ $condition ] && if_true || if_falseсхильний до помилок. У будь-якому випадку мені [ ! -f "$file" ] && if_not_existsлегше читати і розуміти, ніж [ -f "$file" ] || if_not_exists.
Вказано вухами

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

39

Ви можете зробити це:

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

або

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

Якщо ви хочете перевірити файл і папку обох, тоді використовуйте -eопцію замість -f. -eповертає справжнє для звичайних файлів, каталогів, сокет, спеціальних файлів символів, блокує спеціальні файли тощо.


1
Це не стосується bash. Синтаксис походить від оболонки Korn, а також доступний у zsh та bash. [Однак він має обмежену перевагу перед стандартною утилітою тут.
Стефан Хазелас

звичайні файли (як перевірено -f) та каталоги - це лише два з багатьох різних типів файлів . Існують також розетки, символьні посилання, пристрої, фіфоси, двері ... [ -eперевірять наявність файлів (будь-якого типу, включаючи звичайні, фіфо, каталоги ...) після вирішення символьної посилання .
Стефан Хазелас

@StephaneChazelas: я ніде не бачу ksh або zsh тегів. Ми говоримо про bash або щось, що стандартизовано в більшості систем Unix. Отже, в контексті це специфічний удар. ОП не потрібно знати про всі, що там знаходяться снарядів: D
Джахід

Це був коментар до "Баш конкретної" частини вашої відповіді.
Стефан Шазелас

@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

8
Рекомендація полягає в тому, щоб кожна змінна була оточена подвійними лапками, якщо тільки ви точно не знаєте, що у вас є один з рідкісних випадків, коли це непотрібно, або один із рідкісних випадків, коли це шкідливо. (І ні, це не одна з них.)
Уве

Чи хотіли б ви детальніше пояснити, чому це не так, щоб використовувати подвійний лапок? Інакше я не бачу корисності у коментарі.
artdanil

4
Я мав на увазі: Це не один з рідкісних випадків, коли це непотрібно чи шкідливо. Програміст оболонки повинен звикати додавати (майже) кожну змінну до подвійних лапок; цим правилом не обмежується [ ... ].
Уве


24

Існує три різних способи зробити це:

  1. Негатуйте статус виходу за допомогою bash (жодна інша відповідь не сказала цього):

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

    Або:

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

    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 та старших, ви навряд чи натрапите сьогодні на оболонку Борна.
Стефан Хазелас

22

В

[ -f "$file" ]

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

Отже, якщо [ -f "$file" ]повертається true, ви можете сказати, що файл існує і є звичайним файлом або символьним посиланням, яке врешті-решт переходить у звичайний файл (або, принаймні, це було на момент створення stat()).

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

  • файл не існує
  • файл існує, але не є звичайним файлом (це може бути пристрій, 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повідомляє нам, що один із компонентів шляху не є каталогом. Інший випадок, коли ми можемо сказати, що файл не існують цим шляхом). На жаль [команда не дає нам цього знати. Він поверне помилковим, чи код помилки 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!"




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

3
Не ясно, що це додає нічого, чого інші відповіді ще не дали. Він жорстко кодує ім'я шляху. Оскільки питання є тегом bash , воно може використовуватись read -p "Enter file name: " -r aяк підказки, так і читання. Він використовує лапки навколо змінної; це добре, але слід пояснити. Можливо, буде краще, якби це повторило ім'я файлу. І це перевіряє, чи файл існує і чи не порожній (у цьому сенс -s), тоді як питання задає будь-який файл, порожній чи ні (для якого -fбільше підходить).
Джонатан Леффлер

4

іноді може бути зручно використовувати && та || оператори.

Як у (якщо у вас є команда "test"):

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

Примітка: (...) працює в нижній частині корпусу, {...;} працює в одній оболонці. Позначення фігурної дужки працює лише в башті.


1
Ні, позначення фігурної дужки не є лише ударом; він працює в будь-якій сумісній з POSIX оболонці.
Чарльз Даффі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.