Я пытаюсь проверить, существует ли файл, но с подстановочным знаком. Вот мой пример:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Я также пробовал это без двойных кавычек.
Я пытаюсь проверить, существует ли файл, но с подстановочным знаком. Вот мой пример:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Я также пробовал это без двойных кавычек.
Простейшим может быть полагаться на возвращаемое значение ls
(он возвращает ненулевое значение, если файлы не существуют):
if ls /path/to/your/files* 1> /dev/null 2>&1; then
echo "files do exist"
else
echo "files do not exist"
fi
Я перенаправил вывод ls
, чтобы сделать его полностью бесшумным.
EDIT: поскольку этот ответ получил некоторое внимание (и очень полезные комментарии критика в качестве комментариев), вот оптимизация, которая также зависит от расширения glob, но избегает использования ls
:
for f in /path/to/your/files*; do
## Check if the glob gets expanded to existing files.
## If not, f here will be exactly the pattern above
## and the exists test will evaluate to false.
[ -e "$f" ] && echo "files do exist" || echo "files do not exist"
## This is all we needed to know, so we can break after the first iteration
break
done
Это очень похоже на ответ @grok12, но он избегает ненужной итерации по всему списку.
Если ваша оболочка имеет параметр nullglob, и она включена, шаблон шаблона, который не соответствует никаким файлам, будет полностью удален из командной строки. Это приведет к тому, что ls не увидите аргументы pathname, перечислите содержимое текущего каталога и преуспеть, что неверно. GNU stat, который всегда терпит неудачу, если не задано никаких аргументов или аргумент, именующий несуществующий файл, будет более надежным. Кроме того, оператор перенаправления & > представляет собой bashism.
if stat --printf='' /path/to/your/files* 2>/dev/null
then
echo found
else
echo not found
fi
Лучше все еще находится GNU find, который может обрабатывать поиск подстановочных знаков внутри себя и выходить, как только он находит один соответствующий файл, а не тратит время на обработку потенциально огромного списка из них, расширенного оболочкой; это также позволяет избежать риска того, что оболочка может переполнить свой буфер командной строки.
if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)"
then
echo found
else
echo not found
fi
В версиях find, отличных от GNU, не может быть использована опция -maxdepth, чтобы сделать find поиск только /dir/to/search, а не все дерево каталогов, внедренное там.
Вот мой ответ -
files=(xorg-x11-fonts*)
if [ -e "${files[0]}" ];
then
printf "BLAH"
fi
for i in xorg-x11-fonts*; do
if [ -f "$i" ]; then printf "BLAH"; fi
done
Это будет работать с несколькими файлами и с пробелом в именах файлов.
Вы можете сделать следующее:
set -- xorg-x11-fonts*
if [ -f "$1" ]; then
printf "BLAH"
fi
Это работает с sh и производными: ksh и bash. Он не создает никаких вложенных оболочек. Команды $ (..) и '...', используемые в других решениях, создают вложенную оболочку: они разветвляют процесс, и они неэффективны. Конечно, он работает с несколькими файлами, и это решение может быть самым быстрым или вторым по скорости.
Это работает тоже, когда нет совпадений. Нет необходимости использовать nullglob, как говорит один из коментаторов. $ 1 будет содержать оригинальное имя теста, поэтому тест -f $ 1 не будет успешным, потому что файл $ 1 не существует.
UPDATE:
Хорошо, теперь у меня определенно есть решение:
files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
echo "Exists"
else
echo "None found."
fi
> Exists
Возможно, это поможет кому-то:
if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Вопрос не был специфичен для Linux/ Bash, поэтому я подумал, что добавлю способ Powershell - который обрабатывает разные символы - вы помещаете в следующие цитаты ниже:
If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){
Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf
Remove-Item -force -v -path ./output/test-pdf-docx/*.docx
}
Я думаю, что это полезно, потому что концепция исходного вопроса охватывает "оболочки" вообще не только Bash, но и Linux, и будет применяться к пользователям Powershell с тем же вопросом.
Строго говоря, если вы хотите только напечатать "Бла", вот решение:
find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit
Вот еще один способ:
doesFirstFileExist(){
test -e "$1"
}
if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi
Но я думаю, что наиболее оптимальным является следующее, потому что оно не будет пытаться сортировать имена файлов:
if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ]
then printf "BLAH"
fi
Код bash, который я использую
if ls /syslog/*.log > /dev/null 2>&1; then
echo "Log files are present in /syslog/;
fi
Спасибо!
Вот решение для вашей конкретной проблемы, которая не требует циклов for
или внешних команд, таких как ls
, find
и т.п.
if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Как вы можете видеть, это немного сложнее, чем то, на что вы надеялись, и полагается на то, что если оболочка не может расширить glob, это означает, что файлы с этим глобусом не существуют и echo
выведет glob как есть, что позволяет нам просто сравнивать строки, чтобы проверить, существует ли какой-либо из этих файлов вообще.
Если бы мы обобщали процедуру, мы должны учитывать тот факт, что файлы могут содержать пробелы в своих именах и/или путях и что glob char может по праву расшириться до нуля (в вашем примере это будет случай файла, чье имя точно соответствует xorg-x11-шрифтам).
Этого можно достичь с помощью следующей функции: bash.
function doesAnyFileExist {
local arg="$*"
local files=($arg)
[ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ]
}
Возвращаясь к вашему примеру, его можно вызвать так.
if doesAnyFileExist "xorg-x11-fonts*"; then
printf "BLAH"
fi
Расширение Glob должно происходить внутри самой функции, чтобы она работала правильно, поэтому я помещаю аргумент в кавычки и что для него есть первая строка в теле функции: так что любые несколько аргументов (что может быть результатом расширения glob вне функции, а также паразитный параметр) будут объединены в один. Другой подход может заключаться в том, чтобы вызвать ошибку, если имеется более одного аргумента, а другой - игнорировать все, кроме первого аргумента.
Вторая строка в теле функции устанавливает files
var в массив , состоящий из всех имен файлов, которые расширил glob, по одному для каждого элемента массива. Хорошо, если имена файлов содержат пробелы, каждый элемент массива будет содержать имена как есть, включая пробелы.
Третья строка в теле функции выполняет две вещи:
Сначала он проверяет, существует ли более одного элемента в массиве. Если это так, это означает, что glob уверенно получил что-то (из-за того, что мы сделали на 1-й строке), что, в свою очередь, означает, что существует хотя бы один файл, соответствующий глобусу, что мы все хотели знать.
Если на шаге 1. мы обнаружили, что мы получили менее 2 элементов в массиве, тогда мы проверяем, получили ли мы его, и если да, то мы проверяем, существует ли тот, обычный путь. Нам нужно выполнить эту дополнительную проверку, чтобы учесть аргументы аргументов без glob, в этом случае массив содержит только один элемент нерасширенный.
Я использую это:
filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`
if [ $filescount -gt 0 ]; then
blah
fi
ИМХО лучше использовать find
всегда при тестировании файлов, глобусов или каталогов. Камнем преткновения при этом является find
статус выхода: 0, если все пути пройдены успешно, > 0 в противном случае. Выражение, переданное в find
, не создает эха в его коде выхода.
В следующем примере проверяется, есть ли в каталоге записи:
$ mkdir A
$ touch A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty'
not empty
Если A
не имеет файлов grep
завершается сбой:
$ rm A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty'
empty
Если A
не существует grep
снова не работает, потому что find
печатает только stderr:
$ rmdir A
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' || echo 'empty'
find: 'A': No such file or directory
empty
Замените -not -empty
любым другим выражением find
, но будьте осторожны, если вы -exec
команда, которая печатает на stdout. Вы можете захотеть grep для более конкретного выражения в таких случаях.
Этот подход хорошо работает в сценариях оболочки. Первоначально вопрос заключался в поиске glob xorg-x11-fonts*
:
if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q .
then
: the glob matched
else
: ...not
fi
Обратите внимание, что else-разветвлено, если xorg-x11-fonts*
не соответствует, или find
обнаружил ошибку. Чтобы отличить случай, используйте $?
.
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
Попробуйте это
fileTarget="xorg-x11-fonts*"
filesFound=$(ls $fileTarget) # 2014-04-03 edit 2: removed dbl-qts around $(...)
edit 2014-04-03 (удалены dbl-кавычки и добавлен тестовый файл "Charlie 22.html" (2 пробела)
case ${filesFound} in
"" ) printf "NO files found for target=${fileTarget}\n" ;;
* ) printf "FileTarget Files found=${filesFound}\n" ;;
esac
Test
fileTarget="*.html" # where I have some html docs in the current dir
FileTarget Files found=Baby21.html
baby22.html
charlie 22.html
charlie21.html
charlie22.html
charlie23.html
fileTarget="xorg-x11-fonts*"
NO files found for target=xorg-x11-fonts*
Обратите внимание, что это работает только в текущем каталоге или где var fileTarget
содержит путь, который вы хотите проверить.
Как насчет
if ls -l | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob
then
# do something
else
# do something else
fi
Если в сетевой папке огромное количество файлов с использованием подстановочного знака сомнительно (скорость или переполнение аргументов командной строки).
Я закончил с:
if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
echo Such file exists
fi
Вы также можете вырезать другие файлы
if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then
...
fi
Использование новых необычных функций shmancy в оболочках ksh, bash и zsh (этот пример не обрабатывает пробелы в именах файлов):
# Declare a regular array (-A will declare an associative array. Kewl!)
declare -a myarray=( /mydir/tmp*.txt )
array_length=${#myarray[@]}
# Not found if the 1st element of the array is the unexpanded string
# (ie, if it contains a "*")
if [[ ${myarray[0]} =~ [*] ]] ; then
echo "No files not found"
elif [ $array_length -eq 1 ] ; then
echo "File was found"
else
echo "Files were found"
fi
for myfile in ${myarray[@]}
do
echo "$myfile"
done
Да, это пахнет как Perl. Рад, что я не вмешался в это;)
Нашел пару аккуратных решений, которыми стоит поделиться. Первый по-прежнему страдает от проблемы "это сломается, если совпадений слишком много":
pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"
(Напомним, что если вы используете массив без синтаксиса [ ]
, вы получите первый элемент массива.)
Если в вашем скрипте есть "shopt -s nullglob", вы можете просто сделать:
matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"
Теперь, если в каталоге может быть тонна файлов, вы довольно сильно застряли с помощью find:
find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'
мужской тест
if [ -e file ]; then
...
fi
будет работать в dir\файле.
рассматривает