Проверьте, есть ли у Глобуса какие-либо совпадения в bash


если я хочу проверить наличие одного файла, я могу проверить его с помощью test -e filename или [ -e filename ].

Предположим, у меня есть глобус, и я хочу знать, существуют ли какие-либо файлы, имена которых соответствуют глобусу. Глобус может соответствовать 0 файлам (в этом случае мне ничего не нужно делать), или он может соответствовать 1 или более файлам (в этом случае мне нужно что-то сделать). Как я могу проверить, есть ли у Глобуса какие-либо совпадения? (Мне все равно, сколько матчей есть, и было бы лучше, если бы я мог это сделать один if заявление и никаких циклов (просто потому, что я считаю, что наиболее читаемым).

(test -e glob* сбой, если глобус соответствует более чем одному файлу.)

18   169  

18 ответов:

Баш конкретное решение:

compgen -G "<glob-pattern>"

Escape шаблон или он будет предварительно расширен в матчи.

статус выхода:

  • 1 для no-match,
  • 0 для 'одного или нескольких матчей'

stdout список файлы с Глоб.
Я думаю, что это лучший вариант с точки зрения краткости и минимизации потенциальных побочных эффекты.

обновление: пример использования запрашиваемых.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi

опция оболочки nullglob действительно является башизмом.

чтобы избежать необходимости утомительного сохранения и восстановления состояния nullglob, я бы только установил его внутри подрешетки, которая расширяет глобус:

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

для лучшей переносимости и более гибкий подстановка, используйте найти:

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

явно - print-quit действия используются для найти вместо неявного значения по умолчанию - print действий, так что найти завершится, как только он найдет первый файл, соответствующий критериям поиска. Где много файлов совпадают, это должно работать намного быстрее, чем echo glob* или ls glob* и это также позволяет избежать возможности переполнения расширенной командной строки (некоторые оболочки имеют ограничение длины 4K).

если найти похоже на перебор, и количество файлов, которые могут совпадать, невелико, используйте stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi

мне нравится

exists() {
    [ -e "" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

Это одновременно читабельно и эффективно (если нет огромного количества файлов).
Главный недостаток заключается в том, что он гораздо более тонкий, чем кажется, и я иногда чувствую себя вынужденным добавить длинный комментарий.
Если есть совпадение, "glob*" расширяется оболочкой и все совпадения передаются в exists(), который проверяет первый и игнорирует остальные.
Если нет совпадений,"glob*" перешло к exists() и обнаружил, что там не существует любой.

Edit: может быть ложное срабатывание, см. комментарий

test-e имеет неудачную оговорку, что он считает, что сломанные символические ссылки не существуют. Так что вы можете проверить их тоже.

function globexists {
  test -e "" -o -L ""
}

if globexists glob*; then
    echo found
else
    echo not found
fi

У меня есть еще одно решение:

if [ "$(echo glob*)" != 'glob*' ]

Это прекрасно работает для меня. Есть некоторые случаи я пропустил?

на основе ответ flabdablet для меня это выглядит как простой (не обязательно быстрый) просто использовать найти сам, оставляя расширение глобуса на оболочке, например:

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

или if как:

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi

Если у вас есть набор globfail вы можете использовать этот сумасшедший (который вы действительно не должны )

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

или

q=( * ) && echo 0 || echo 1

чтобы несколько упростить ответ Майна, основываясь на его идее:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi

эта мерзость, кажется, работает:

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

Это, вероятно, требует bash, а не sh.

это работает, потому что параметр nullglob заставляет глобус вычислять пустую строку, если нет совпадений. Таким образом, любой непустой вывод из команды echo указывает, что глобус соответствует чему-то.

Я не видел этого ответа, так что я думал, что я положил его там:

set -- glob*
[ -f "" ] && echo "found [email protected]"
(ls glob* &>/dev/null && echo Files found) || echo No file found
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

обратите внимание, что это может быть очень время cosuming, если есть много совпадений или доступ к файлам медленный.

#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi

[ ls glob* 2>/dev/null | head -n 1 ] & & echo true

в Bash, вы можете глобус в массив; если глобус не соответствует, Ваш массив будет содержать одну запись, которая не соответствует существующему файлу:

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

Примечание: Если у вас есть nullglob set,scripts будет пустой массив, и вы должны проверить с [ "${scripts[*]}" ] или [ "${#scripts[*]}" != 0 ] вместо. Если вы пишете библиотеку, которая должна работать с или без nullglob, вы хотите

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

преимущество такого подхода заключается в том, что у вас есть список файлов, которые вы хотите работа с, а не повторять операцию Глоб.

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

set -- glob* если [- f "$1"]; то е "мля" интернет

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

когда нет совпадения FPR glob* $1 будет содержать ' glob*'. Тест-f "$1 " не будет истинным, потому что файл glob* не существует. Это нормально для всех случаев

ls | grep -q "glob.*"

Не самое эффективное решение (если в каталоге есть тонна файлов, это может быть медленным), но оно просто, легко читается, а также имеет то преимущество, что регулярные выражения являются более мощными, чем простые шаблоны bash glob.