Проверьте, существует ли файл с подстановочным знаком в сценарии оболочки [дубликат]
этот вопрос уже есть ответ здесь:
Я пытаюсь проверить, существует ли файл, но с подстановочным знаком. Вот мой пример:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Я также пробовал без двойных кавычек.
21 ответ:
самым простым должно быть полагаться на
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 не смотрите аргументы пути, перечислите содержимое текущего каталога и преуспейте, что неверно. GNU стат, который всегда терпит неудачу, если не заданы аргументы или аргумент, называющий несуществующий файл, будет более надежным. Кроме того, оператор &> redirection является bashism.
if stat --printf='' /path/to/your/files* 2>/dev/null then echo found else echo not found fi
еще лучше GNU найти, который может обрабатывать подстановочный поиск внутри и выходить, как только он находит один соответствующий файл, а не тратить время на обработку потенциально огромного списка из них, расширенного оболочкой; это также позволяет избежать риска того, что оболочка может переполнить буфер командной строки.
if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)" then echo found else echo not found fi
не-GNU версии найти может не иметь - maxdepth здесь сделай найти искать только в /dir / to / search вместо того, чтобы все дерево каталогов коренится там.
for i in xorg-x11-fonts*; do if [ -f "$i" ]; then printf "BLAH"; fi done
Это будет работать с несколькими файлами и с пробелами в именах файлов.
обновление:
хорошо, теперь у меня определенно есть решение:
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
вы можете сделать следующее:
set -- xorg-x11-fonts* if [ -f "" ]; then printf "BLAH" fi
это работает с sh и производными: ksh и bash. Он не создает никакой суб-оболочки. $(..)и.` .."команды создают суб-оболочку: они разветвляют процесс, и они неэффективны. Конечно, он работает с несколькими файлами, и это решение может быть самым быстрым или вторым по скорости.
Он тоже работает, когда нет совпадений. Нет необходимости использовать nullglob, как говорит один из комментаторов. $1 будет содержать оригинал имя теста, поэтому тест-f $1 не будет успешным, потому что файл $1 не существует.
вопрос не был специфичен для 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 "" } 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
как вы можете видеть, это просто немного сложнее, чем то, на что вы надеялись, и полагается на то, что если оболочка не может расширить глобус, это означает, что никаких файлов с этим глобусом не существует и
echo
выведет Глоб как, что позволяет нам сделать простое сравнение строк с проверьте, существует ли вообще какой-либо из этих файлов.если бы мы обобщили процедуру однако, мы должны принять во внимание тот факт, что файлы может содержать пробелы в пределах их имен и / или путей и что глобус char может по праву расширяться до нуля (в вашем примере это было бы в случае файла, имя которого ровно настройка xorg-Х11-шрифты).
это может быть достигнуто с помощью следующей функции, в Баш.
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 вне функции, а также паразитного параметра) будут объединены в один. Другой подход может заключаться в том, чтобы вызвать ошибку, если есть более одного аргумента, еще один может игнорировать все, кроме 1-го аргумента.
вторая строка в теле функции определяет
files
ВАР к массив состоит из всех имен файлов, которые глобус расширился до, по одному для каждого элемента массива. это нормально, если имена файлов содержат пробелы, каждый элемент массива будет содержать имена как, включая пробелы.в третья строка в теле функции делает две вещи:
он сначала проверяет, есть ли более одного элемента в массиве. Если так, то это означает Глоб конечно расширился до чего-то (из-за того, что мы сделали на 1-й строке), что, в свою очередь, означает, что существует хотя бы один файл, соответствующий глобусу, и это все, что мы хотели знать.
если на шаге 1. мы обнаружили, что получили менее 2 элементов в массиве, затем мы проверяем, есть ли он у нас, и если да, то мы проверяем, существует ли он, обычным способом. Нам нужно сделать эту дополнительную проверку, чтобы учесть аргументы функции без Глоб символов, в этом случае массив содержит только один, нерасширенные элемент.
Я использую этот:
filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'` if [ $filescount -gt 0 ]; then blah fi
ИМХО лучше использовать
find
всегда при тестировании файлов, глобусов или каталогов. Камнем преткновения при этом являетсяfind
'S состояние выхода: 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 для более конкретного выражения.этот подход хорошо работает в шелл-скриптах. Первоначально вопрос состоял в том, чтобы искать Глоб
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
обнаружена ошибка. Для различения случая используйте$?
.
попробуй такое
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
тест
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
использование новых причудливых функций 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 /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'