Проверьте, существует ли файл с шаблоном в командной оболочке script

Я пытаюсь проверить, существует ли файл, но с подстановочным знаком. Вот мой пример:

if [ -f "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

Я также пробовал это без двойных кавычек.

Ответ 1

Простейшим может быть полагаться на возвращаемое значение 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, но он избегает ненужной итерации по всему списку.

Ответ 2

Если ваша оболочка имеет параметр 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, а не все дерево каталогов, внедренное там.

Ответ 3

Вот мой ответ -

files=(xorg-x11-fonts*)

if [ -e "${files[0]}" ];
then
    printf "BLAH"
fi

Ответ 4

for i in xorg-x11-fonts*; do
  if [ -f "$i" ]; then printf "BLAH"; fi
done

Это будет работать с несколькими файлами и с пробелом в именах файлов.

Ответ 5

Вы можете сделать следующее:

set -- xorg-x11-fonts*
if [ -f "$1" ]; then
    printf "BLAH"
fi

Это работает с sh и производными: ksh и bash. Он не создает никаких вложенных оболочек. Команды $ (..) и '...', используемые в других решениях, создают вложенную оболочку: они разветвляют процесс, и они неэффективны. Конечно, он работает с несколькими файлами, и это решение может быть самым быстрым или вторым по скорости.

Это работает тоже, когда нет совпадений. Нет необходимости использовать nullglob, как говорит один из коментаторов. $ 1 будет содержать оригинальное имя теста, поэтому тест -f $ 1 не будет успешным, потому что файл $ 1 не существует.

Ответ 6

UPDATE:

Хорошо, теперь у меня определенно есть решение:

files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
   echo "Exists"
else
    echo "None found."
fi

> Exists

Ответ 7

Возможно, это поможет кому-то:

if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

Ответ 8

Вопрос не был специфичен для 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 с тем же вопросом.

Ответ 9

Строго говоря, если вы хотите только напечатать "Бла", вот решение:

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

Ответ 10

Код bash, который я использую

if ls /syslog/*.log > /dev/null 2>&1; then 
   echo "Log files are present in /syslog/; 
fi

Спасибо!

Ответ 11

Вот решение для вашей конкретной проблемы, которая не требует циклов 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, в этом случае массив содержит только один элемент нерасширенный.

Ответ 12

Я использую это:

filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`  
if [ $filescount -gt 0 ]; then  
    blah  
fi

Ответ 13

ИМХО лучше использовать 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 обнаружил ошибку. Чтобы отличить случай, используйте $?.

Ответ 14

if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi

Ответ 15

Попробуйте это

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 содержит путь, который вы хотите проверить.

Ответ 16

Как насчет

if ls -l  | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob
then
     # do something
else
     # do something else
fi 

Ответ 17

Если в сетевой папке огромное количество файлов с использованием подстановочного знака сомнительно (скорость или переполнение аргументов командной строки).

Я закончил с:

if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
  echo Such file exists
fi

Ответ 18

Вы также можете вырезать другие файлы

if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then
   ...
fi

Ответ 19

Использование новых необычных функций 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. Рад, что я не вмешался в это;)

Ответ 20

Нашел пару аккуратных решений, которыми стоит поделиться. Первый по-прежнему страдает от проблемы "это сломается, если совпадений слишком много":

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'

Ответ 21

мужской тест

if [ -e file ]; then
...  
fi

будет работать в dir\файле.

рассматривает