Я хочу проверить текущий каталог на наличие файлов с расширениями abc
, bak
или tmp
,илифайл с именем tmpout.wrk
. Я не могу заставить это (в конечном итоге часть функции) работать в zsh. Он запускается, но не может правильно определить.
if [[ -f *.(abc|bak|tmp) || -f tmpout.wrk ]]; then
echo 'true';
else
echo 'false';
fi
решение1
Чтобы проверить, что глоб возвращает хотя бы один файл, можно сделать следующее:
if ()(($#)) (*.(abc|bak|tmp)|tmpout.wrk)(NY1); then
echo true
else
echo false
fi
Чтобы проверить, что хотя бы один из них является обычным файлом после разрешения символической ссылки, добавьте -.
квалификатор glob:
if ()(($#)) (*.(abc|bak|tmp)|tmpout.wrk)(NY1-.); then
echo true
else
echo false
fi
()(($#))
является анонимной функцией, которой мы передаем результат globs. Тело этой функции ((($#))
) просто проверяет, что число аргументов не равно нулю.N
поскольку для этого глобуса включается квалификатор глобусаnullglob
(заставляет глоб расширяться до нуля, если он не соответствует ни одному файлу)Y1
ограничивает расширение максимум одним файлом. Это оптимизация производительности.-
заставляет следующего квалификатора глобуса рассматриватьсяпослеразрешение символической ссылки..
рассматривает только обычные файлы (поэтому здесь обычные файлы или символические ссылки в конечном итоге разрешаются в обычный файл, как[ -f file ]
это делает команда).
решение2
TL;DR
set -o extendedglob
if [[ -n *.(abc|bak|tmp)(#qN) || -f tmpout.wrk ]]; then
В противном случае, с помощью некоторых тестов,
% [[ -f /etc/passwd ]] && echo yea
yea
% echo /etc/passw?
/etc/passwd
% [[ -f /etc/passw? ]] && echo yea
%
Ладно, что zsh
здесь делается?
% set -x
% [[ -f /etc/passw? ]] && echo yes
+zsh:13> [[ -f '/etc/passw?' ]]
%
Эти одинарные кавычки, конечно, ничего не заменят. Давайте поищем [[
в man zshall
... и затем поищем CONDITIONAL EXPRESSIONS
... ах, вот кое-что о генерации имени файла:
Filename generation is not performed on any form of argument to condi-
tions. However, it can be forced in any case where normal shell expan-
sion is valid and when the option EXTENDED_GLOB is in effect by using
an explicit glob qualifier of the form (#q) at the end of the string.
A normal glob qualifier expression may appear between the `q' and the
closing parenthesis; if none appears the expression has no effect
beyond causing filename generation. The results of filename generation
are joined together to form a single word, as with the results of other
forms of expansion.
This special use of filename generation is only available with the [[
syntax. If the condition occurs within the [ or test builtin commands
then globbing occurs instead as part of normal command line expansion
before the condition is evaluated. In this case it may generate multi-
ple words which are likely to confuse the syntax of the test command.
For example,
[[ -n file*(#qN) ]]
produces status zero if and only if there is at least one file in the
current directory beginning with the string `file'. The globbing qual-
ifier N ensures that the expression is empty if there is no matching
file.
Итак, имея это в виду,
% [[ -f /etc/passw?(#q) ]] && echo yes
+zsh:14> [[ -f /etc/passwd ]]
+zsh:14> echo yes
yes
% exec zsh -l
И в вашем случае, учитывая тот случай, когда файлов может не быть:
% mkdir dir
% cd dir
% touch blah.foo
% [[ -f *.(foo|bar|baz)(#q) ]] && echo yea
yea
% rm blah.foo
% [[ -f *.(foo|bar|baz)(#q) ]] && echo yea
zsh: no matches found: *.(foo|bar|baz)(#q)
% [[ -f *.(foo|bar|baz)(#qN) ]] && echo yea
% touch a.foo b.foo
% [[ -f *.(foo|bar|baz)(#qN) ]] && echo yea
% [[ -n *.(foo|bar|baz)(#qN) ]] && echo yea
yea
%
(хотя при этом -n
мы проверяем только совпадение шаблонов, а не то, являются ли соответствующие файлы обычными файлами).