Bash если файл не пустой
практически любой язык программирования включает в себя условные операторы, предназначенные для проверки условий, чтобы выбрать тот или иной путь развития событий в зависимости от этих условий. В Bash, для проверки условий, имеется команда test, различного вида скобочные операторы и условный оператор if/then.
Оператор if/then проверяет -- является ли код завершения списка команд 0 (поскольку 0 означает "успех" ), и если это так, то выполняет одну, или более, команд, следующие за словом then.
Существует специальная команда -- [ (левая квадратная скобка). Она является синонимом команды test, и является встроенной командой (т.е. более эффективной, в смысле производительности). Эта команда воспринимает свои аргументы как выражение сравнения или как файловую проверку и возвращает код завершения в соответствии с результатами проверки (0 -- истина, 1 -- ложь).
Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конструкцию [[ . ]] расширенный вариант команды test , которая выполняет сравнение способом более знакомым программистам, пишущим на других языках программирования. Обратите внимание: [[ -- это зарезервированное слово, а не команда.
Bash исполняет [[ $a -lt $b ]] как один элемент, который имеет код возврата.
Круглые скобки (( . )) и предложение let . так же возвращают код 0 , если результатом арифметического выражения является ненулевое значение. Таким образом, арифметические выражения могут учавствовать в операциях сравнения.
Условный оператор if проверяет код завершения любой команды, а не только результат выражения, заключенного в квадратные скобки.
Оператор if/then допускает наличие вложенных проверок.
Это детальное описание конструкции "if-test" любезно предоставлено Stephane Chazelas.
Пример 7-1. Что есть "истина"?
Упражнение. Объясните результаты, полученные в Пример 7-1.
Когда if и then располагаются в одной строке, то конструкция if должна завершаться точкой с запятой. И if , и then -- это зарезервированные слова. Зарезервированные слова начинают инструкцию, которая должна быть завершена прежде, чем в той же строке появится новая инструкция.
Else if и elif
elif -- это краткая форма записи конструкции else if . Применяется для построения многоярусных инструкций if/then .
Конструкция if test condition-true является точным эквивалентом конструкции if [ condition-true ], где левая квадратная скобка [ выполняет те же действия, что и команда test. Закрывающая правая квадратная скобка ] не является абсолютно необходимой, однако, более новые версии Bash требуют ее наличие.
Команда test -- это встроенная команда Bash, которая выполняет проверки файлов и производит сравнение строк. Таким образом, в Bash-скриптах, команда test не вызывает внешнюю ( /usr/bin/test) утилиту, которая является частью пакета sh-utils . Аналогично, [ не производит вызов утилиты /usr/bin/[, которая является символической ссылкой на /usr/bin/test.
Пример 7-2. Эквиваленты команды test -- /usr/bin/test, [ ] , и /usr/bin/[
Конструкция [[ ]] более универсальна, по сравнению с [ ] . Этот расширенный вариант команды test перекочевал в Bash из ksh88 .
Внутри этой конструкции не производится никакой дополнительной интерпретации имен файлов и не производится разбиение аргументов на отдельные слова, но допускается подстановка параметров и команд.
Строго говоря, после оператора if, ни команда test, ни квадратные скобки ( [ ] или [[ ]] ) не являются обязательными.
Инструкция "if COMMAND" возвращает код возврата команды COMMAND.
Точно так же, условие, находящееся внутри квадратных скобок может быть проверено без использования оператора if.
Внутри (( )) производится вычисление арифметического выражения. Если результатом вычислений является ноль, то возвращается 1 , или "ложь" . Ненулевой результат дает код возврата 0 , или "истина" . То есть полная противоположность инструкциям test и [ ] , обсуждавшимся выше.
я использовал следующий скрипт, чтобы узнать, существует ли файл:
какой правильный синтаксис использовать, если я хочу только проверить, делает ли файл не?
Тестирование Файлов Bash
-b filename - специальный блочный файл
-c filename - специальный символьный файл
-d directoryname - проверьте наличие каталога
-e filename - проверка на существование файла, независимо от типа (узел, каталог, розетки и т. д.)
-f filename - проверьте наличие обычного файла, а не каталога
-G filename - проверьте, существует ли файл и принадлежит эффективной группе ID
-G filename set-group-id - True, если файл существует и set-group-id
-k filename - липкий бит
-L filename - символическая ссылка
-O filename - True, если файл существует и принадлежит эффективному id пользователя
-r filename - проверьте, является ли файл читаемым
-S filename - проверьте, является ли файл socket
-s filename - проверьте, если файл имеет ненулевой размер
-u filename - проверьте, установлен ли бит file set-user-id
-w filename - Проверьте, доступен ли файл для записи
-x filename - проверьте, является ли файл исполняемым
как использовать:
A тест выражение можно отрицать с помощью ! оператор
вы можете отрицать выражение с помощью "!":
соответствующие man-страницы man test или, что эквивалентно, man [ -- или help test или help [ для встроенной команды bash.
кроме того, возможно, что файл является сломанной символической ссылкой или нерегулярным файлом, например, сокетом, устройством или fifo. Например, чтобы добавить чек на сломанные символические ссылки:
стоит отметить, что если вам нужно выполнить одну команду можно сократить
Я предпочитаю делать следующий однострочный, в POSIX shell совместимый формат:
для нескольких команд, как я бы сделал в скрипте:
Как только я начал это делать,я редко использую полностью типизированный синтаксис!!
чтобы проверить существование файла, параметр может быть одним из следующих:
все тесты ниже применяются к обычным файлам, каталогам и символическим ссылкам:
вы должны быть осторожны при запуске test для некотируемой переменной, поскольку это может привести к неожиданным результатам:
рекомендация обычно состоит в том, чтобы тестируемая переменная была окружена двойными кавычками:
существуют три различных способа сделать это:
отрицание статуса выхода с bash (ни один другой ответ не сказал этого):
действуйте на результат отрицательного теста ( || вместо && ):
это выглядит глупо (IMO), не используйте его, если ваш код не должен быть переносным в оболочку Bourne (например, /bin/sh Solaris 10 или более ранних версий), которому не хватало оператора отрицания трубопровода ( ! ):
вы можете сделать это:
если вы хотите проверить файл и папку, а затем использовать вместо -f . -e возвращает true для обычных файлов, каталогов, сокетов, специальных файлов символов, специальных файлов блоков и т. д.
the тут stat() (не lstat() ) системный вызов по пути, хранящемуся в $file и возвращает правда если этот системный вызов выполняется успешно и тип файла, возвращаемого stat() - это "регулярные".
если [ -f "$file" ] возвращает true, вы можете сказать, файл существует и является обычным файлом или символической ссылкой, в конечном итоге, решать в обычный файл (или по крайней мере это было на момент stat() ).
- файл не существует
- файл существует, но не является обычным файлом
- файл существует, но у вас нет разрешения на поиск в Родительском каталоге
- файл существует, но этот путь для доступа к нему слишком длинный
- файл является символической ссылкой на обычный файл, но у вас нет разрешения на поиск для некоторых каталогов, участвующих в разрешении символической ссылки.
- . любая другая причина, почему stat() системный вызов может завершиться неудачей.
короче говоря, это должно быть:
чтобы знать наверняка, что файл не существует, нам понадобится stat() системный вызов для возврата с кодом ошибки ENOENT ( ENOTDIR говорит нам, что один из компонентов пути не является каталогом, это еще один случай, когда мы можем сказать, что файл не существует по этому пути). К сожалению, [ команда не дает нам знать об этом. Он вернет false, является ли код ошибки ENOENT, EACCESS (разрешение отказано), ENAMETOOLONG или что-либо еще.
на [ -e "$file" ] тест также можно сделать с ls -Ld -- "$file" > /dev/null . В таком случае . --20--> скажет вам, почему stat() не удалось, хотя информация может быть легко использована программно:
по крайней мере ls говорит мне, что это не потому, что файл не существует, что он терпит неудачу. Потому что он не может сказать, существует файл или нет. The [ команда просто проигнорировала проблему.
С zsh shell, вы можете запросить код ошибки с помощью $ERRNO специальная переменная после сбоя [ команда и декодировать это число с помощью $errnos специальный массив в zsh/system модуль:
чтобы отменить тест, используйте "!". Это эквивалентно логическому оператору" не " на других языках. Попробуйте это:
или написано немного по-другому:
или вы можете использовать:
или, пресинг все вместе:
который может быть записан (используя тогда "и" оператор:&&) как:
, который выглядит короче такой:
на test что может рассчитывать. Это сработало для меня (на основе Bash Shell: проверить файл существует или нет):
I've used the following script to see if a file exists:
What's the correct syntax to use if I only want to check if the file does not exist?
Being the very lazy person that I am, I would typically have used the following silly workaround construct: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi; Probably good that I found this question instead and learned to do it in a more proper way. :)
To be pendantic, you should say "regular file", as most UNIX/POSIX docs refer generically to all types of file system entries a simply "files", e.g., a symbolic link is a type of a file, as is a named pipe, regular file, directory, block special, character special, socket, etc.
@kevinarpe if you want to test whether something exists, use -e . -f won't pick up directories, symlinks, etc.
To be safe, always use double quotes to correctly handle file names with whitespace, e.g., FILE=$1 -> FILE="$1" and if [ -f $FILE ]; -> if [ -f "$FILE" ];
Выводы
В статье была рассмотрена проверка существования файла bash, а также его пустоты. Обе функции дополняют друг друга, поэтому использовать их в связке - эффективный приём.
Хороший тон в написании сценариев командного интерпретатора - сначала определить тип файла и его дальнейшую роль в программе, а затем уже проверять объект на существование.
Этот ключ может использоваться для проверки -- является ли файл стандартным устройством ввода stdin ( [ -t 0 ]) или стандартным устройством вывода stdout ( [ -t 1 ]).
файл доступен для чтения ( пользователю, запустившему сценарий )
файл доступен для записи (пользователю, запустившему сценарий)
файл доступен для исполнения (пользователю, запустившему сценарий)
set-group-id (sgid) флаг для файла или каталога установлен
Если для каталога установлен флаг sgid, то файлы, создаваемые в таком каталоге, наследуют идентификатор группы каталога, который может не совпадать с идентификатором группы, к которой принадлежит пользователь, создавший файл. Это может быть полезно для каталогов, в которых хранятся файлы, общедоступные для группы пользователей.
set-user-id (suid) флаг для файла установлен
Установленный флаг suid приводит к изменению привилегий запущенного процесса на привилегии владельца исполняемого файла. Исполняемые файлы, владельцем которых является root , с установленным флагом set-user-id запускаются с привилегиями root , даже если их запускает обычный пользователь. [1] Это может оказаться полезным для некоторых программ (таких как pppd и cdrecord), которые осуществляют доступ к аппаратной части компьютера. В случае отсутствия флага suid , программы не смогут быть запущены рядовым пользователем, не обладающим привилегиями root.
Файл с установленным флагом suid отображается с включенным флагом s в поле прав доступа.
флаг sticky bit (бит фиксации) установлен
Общеизвестно, что флаг "sticky bit" -- это специальный тип прав доступа к файлам. Программы с установленным флагом "sticky bit" остаются в системном кэше после своего завершения, обеспечивая тем самым более быстрый запуск программы. [2] Если флаг установлен для каталога, то это приводит к ограничению прав на запись. Установленный флаг "sticky bit" отображается в виде символа t в поле прав доступа.
Если пользователь не является владельцем каталога, с установленным "sticky bit", но имеет право на запись в каталог, то он может удалять только те файлы в каталоге, владельцем которых он является. Это предотвращает удаление и перезапись "чужих" файлов в общедоступных каталогах, таких как /tmp.
вы являетесь владельцем файла
вы принадлежите к той же группе, что и файл
файл был модифицирован с момента последнего чтения
файлы f1 и f2 являются "жесткими" ссылками на один и тот же файл
"НЕ" -- логическое отрицание (инверсия) результатов всех вышеприведенных проверок (возвращается true если условие отсутствует).
Пример 7-4. Проверка "битых" ссылок
Проверка существования файла Bash
Начать стоит с простого и более общего метода. Параметр -e позволяет определить, существует ли указанный объект. Не имеет значения, является объект каталогом или файлом.
Пример работы кода:
Проверка файла на пустоту
Чтобы определить, является ли файл пустым, нужно выполнить проверку с помощью параметра -s. Это особенно важно, когда файл намечен на удаление. Здесь нужно быть очень внимательным к результатам, так как успешное выполнение этого параметра указывает на наличие данных.
Результат работы программы:
В этом скрипте файл создаётся командой touch, и при первой проверке на пустоту возвращается отрицательный результат. Затем в него записываются данные в виде команды date, после чего повторная проверка файла возвращает истину.
20 Answers 20
The test command ( [ here) has a "not" logical operator which is the exclamation point (similar to many other languages). Try this:
I struggled a bit to find the right syntax for "if any of 2 files does not exist". The following both work: if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
Parameter can be any one of the following: -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
There is an asymmetry in using ! -f with && versus using -f with || . This has to do with the exit code returned by the non/existence check. If you need your line to always exit cleanly with exit code 0 (and sometimes you don't want this constraint), the two approaches are not interchangeable. Alternatively, just use an if statement and you no longer have to worry about the exit code of your non/existence check.
Bash File Testing
-b filename - Block special file
-c filename - Special character file
-d directoryname - Check for directory Existence
-e filename - Check for file existence, regardless of type (node, directory, socket, etc.)
-f filename - Check for regular file existence not a directory
-G filename - Check if file exists and is owned by effective group ID
-G filename set-group-id - True if file exists and is set-group-id
-k filename - Sticky bit
-L filename - Symbolic link
-O filename - True if file exists and is owned by the effective user id
-r filename - Check if file is a readable
-S filename - Check if file is socket
-s filename - Check if file is nonzero size
-u filename - Check if file set-user-id bit is set
-w filename - Check if file is writable
-x filename - Check if file is executable
How to use:
A test expression can be negated by using the ! operator
@0x90 If you want, you are free to edit my post and add it to the list. I guess you mean: -n String - Check if the length of the string isn't zero. Or do you mean file1 -nt file2 - Check if file1 is newer then file 2 (you can also use -ot for older then)
@MzA because we need to make the code readable, nobody knows what is $1. So assign $1 as file and then use that file variable to do something else looks more readable than using an unknown argument $1
Regarding the -G operator, the two are slightly different, as I understand it. going by the "SETUID & SETGID BITS" section of the chmod man-page, the two would give different values in the case of root user, would they not. I am referring specifically to the "unless user has appropriate permissions" part. Regardless, excellent answer. Bookmarking for this alone.
You can negate the expression inside test (for which [ is an alias) with "!":
The relevant man page is man test or, equivalently, man [ -- or help test or help [ for the built-in bash command.
Alternatively (less commonly used) you can negate the result of test using:
That syntax is described in "man 1 bash" in sections "Pipelines" and "Compound Commands".
In bash, [ is a builtin. So the relevant information is rather obtained by help [ . but this shows that [ is a synonym for the test builtin, hence the relevant information is rather obtained by help test . See also the Bash Conditional Expression section in the manual.
@gniourf_gniourf: Yes, but the bash built-in [ command behaves very similarly to the external [ command, so either man test or man [ will give you a good idea of how it works.
@KeithThompson except that the bash builtin [ has more switches than the external command [ found on my system. Generally speaking I believe it's better to read the documentation specific to a given tool, and not the documentation specific to another vaguely related one. I might be wrong, though ;)
Also, it's possible that the file is a broken symbolic link, or a non-regular file, like e.g. a socket, device or fifo. For example, to add a check for broken symlinks:
May I ask why the two "["s in the test? (eg [[ ! -a $FILE ]]). I tried all the options mentioned on a solaris box and only that one worked, so grateful, but why?
according to tldp.org/LDP/abs/html/fto.html -a is identical in effect to -e. It has been "deprecated," and its use is discouraged. Anyhow +1 for mentioning to check on broken symlink too
@dimitrismistriotis two "[" is a non-portable extension implemented (differently) by zsh & bash; generally you should avoid it if at all possible.
See SO 321348 for a good reason why -a can't be negated in single bracket conditional expressions. Avoid -a altogether, I suggest.
It's worth mentioning that if you need to execute a single command you can abbreviate
I prefer to do the following one-liner, in POSIX shell compatible format:
For a couple of commands, like I would do in a script:
Once I started doing this, I rarely use the fully typed syntax anymore!!
First of all, unquoted variable references are error-prone. That said, where does it say in any bash manpage that the [ or test built-in would test for file existence of the argument by default (as opposed to -e )? Would that not be ambiguous? AFAIK (and AIUI the section "CONDITIONAL EXPRESSIONS") the only thing that is tested with your approach is that the argument is not empty (or undefined), which is, in this case, a tautology (let $DIR = '' and $FILE = '' , then the argument is still '//' ).
Proof: ls /foo , result ls: cannot access /foo: No such file or directory . [ /foo ] && echo 42 , result 42 . GNU bash, version 4.2.37(1)-release (i486-pc-linux-gnu).
@PointedEars: I failed to specify the -f option, at the moment I wrote this answer. Obviously you could always use -e , if your not sure it will be a regular file. Additionally In all my scripts I quote these constructs, I must have just submitted this without adequate proofing.
ACK. But you probably know that a one-liner cannot solve the if-else problem: [ $condition ] && if_true || if_false is error-prone. In any event, I find [ ! -f "$file" ] && if_not_exists easier to read and understand than [ -f "$file" ] || if_not_exists .
To test file existence, the parameter can be any one of the following:
All the tests below apply to regular files, directories, and symlinks:
You can do this:
If you want to check for file and folder both, then use -e option instead of -f . -e returns true for regular files, directories, socket, character special files, block special files etc.
That is not bash-specific. The syntax comes from the Korn shell and is also available in zsh and bash. It has limited advantage over the standard [ utility here though.
regular files (as checked by -f ) and directories are just two of many different types of files. There are also sockets, symlinks, devices, fifos, doors. [ -e will test for file existence (of any type including regular, fifo, directory. ) after symlink resolution.
@StephaneChazelas : I don't see any ksh or zsh tag anywhere. We are talking about bash or something that is standardized on most unix systems. So, within the context, it's bash specific. OP doesn't need to know of every whatever shells are out there :D
@StephaneChazelas : Yes, and within context (bash and the standard sh), it's bash specific. I don't see the point in your second comment, it's totally out of context. OP doesn't need -e , he needs -f .
You should be careful about running test for an unquoted variable, because it might produce unexpected results:
The recommendation is usually to have the tested variable surrounded by double quotation marks:
The recommendation is to have every variable surrounded by double quotation marks, unless you know exactly that you have one of the rare cases where it's unnecessary, or one of the even rarer cases where it's harmful. (And no, this is not one of them.)
Would you care to elaborate why this is not the case to use double quotation mark? Otherwise I don't see the usefulness in the comment.
I meant: This is not one of the rare cases where it's unnecessary or harmful. A shell programmer should get used to enclose (almost) every variable in double quotes; this rule is not limited to [ . ] .
the [ command does a stat() (not lstat() ) system call on the path stored in $file and returns true if that system call succeeds and the type of the file as returned by stat() is "regular".
So if [ -f "$file" ] returns true, you can tell the file does exist and is a regular file or a symlink eventually resolving to a regular file (or at least it was at the time of the stat() ).
However if it returns false (or if [ ! -f "$file" ] or ! [ -f "$file" ] return true), there are many different possibilities:
- the file doesn't exist
- the file exists but is not a regular file (could be a device, fifo, directory, socket. )
- the file exists but you don't have search permission to the parent directory
- the file exists but that path to access it is too long
- the file is a symlink to a regular file, but you don't have search permission to some of the directories involved in the resolution of the symlink.
- . any other reason why the stat() system call may fail.
In short, it should be:
To know for sure that the file doesn't exist, we'd need the stat() system call to return with an error code of ENOENT ( ENOTDIR tells us one of the path components is not a directory is another case where we can tell the file doesn't exist by that path). Unfortunately the [ command doesn't let us know that. It will return false whether the error code is ENOENT, EACCESS (permission denied), ENAMETOOLONG or anything else.
The [ -e "$file" ] test can also be done with ls -Ld -- "$file" > /dev/null . In that case, ls will tell you why the stat() failed, though the information can't easily be used programmatically:
At least ls tells me it's not because the file doesn't exist that it fails. It's because it can't tell whether the file exists or not. The [ command just ignored the problem.
With the zsh shell, you can query the error code with the $ERRNO special variable after the failing [ command, and decode that number using the $errnos special array in the zsh/system module:
В операционных системах GNU/Linux любые объекты системы являются файлами. И проверка существования файла bash - наиболее мощный и широко применяемый инструмент для определения и сравнения в командном интерпретаторе.
В рамках интерпретатора Bash, как и в повседневном понимании людей, все объекты файловой системы являются, тем, чем они есть, каталогами, текстовыми документами и т.д. В этой статье будет рассмотрена проверка наличия файла Bash, а также его проверка на пустоту, и для этого используется команда test.
Проверка наличия файла
Проверка файла Bash на то, является ли данный объект файлом (то есть существует ли файл), выполняется с помощью параметра -f.
Пример работы кода:
В сценарии проверяется, является ли $HOME файлом. Результат проверки отрицательный, после чего проверяется настоящий файл .bash_history, что уже возвращает истину.
На заметку: на практике предпочтительнее использовать сначала проверку на наличие объекта как такового, а затем — на его конкретный тип. Так можно избежать различных ошибок или неожиданных результатов работы программы.
Примечания
С флагом suid , на двоичных исполняемых файлах, надо быть очень осторожным, поскольку это может быть небезопасным. Установка флага suid на файлы-сценарии не имеет никакого эффекта.
В современных UNIX-системах, "sticky bit" больше не используется для файлов, только для каталогов.
Читайте также: