Проверьте, есть ли у шара какие-либо совпадения в bash
Если я хочу проверить наличие одного файла, я могу проверить его с помощью test -e filename
или [ -e filename ]
.
Предположим, что у меня есть glob, и я хочу знать, существуют ли какие-либо файлы, чьи имена соответствуют glob. Глобус может соответствовать 0 файлам (в этом случае мне нужно ничего не делать), или он может соответствовать 1 или более файлам (в этом случае мне нужно что-то сделать). Как я могу проверить, имеет ли glob какие-либо совпадения? (Мне все равно, сколько совпадений есть, и было бы лучше, если бы я мог сделать это с помощью одного оператора if
и без циклов (просто потому, что я нахожу это наиболее читаемым).
(test -e glob*
завершается сбой, если glob соответствует нескольким файлам.)
Ответы
Ответ 1
Bash конкретное решение:
compgen -G "<glob-pattern>"
Побег шаблон или он будет предварительно расширен в спички.
Статус выхода:
- 1 для несоответствия,
- 0 за "один или несколько матчей"
stdout
- это список файлов, соответствующих глобусу.
Я думаю, что это лучший вариант с точки зрения краткости и минимизации потенциальных побочных эффектов.
ОБНОВЛЕНИЕ: пример использования запрашивается.
if compgen -G "/tmp/someFiles*" > /dev/null; then
echo "Some files exist."
fi
Ответ 2
Опция оболочки nullglob действительно является башизмом.
Чтобы избежать необходимости утомительного сохранения и восстановления состояния nullglob, я только установил его внутри подоболочки, которая расширяет glob:
if test -n "$(shopt -s nullglob; echo glob*)"
then
echo found
else
echo not found
fi
Для лучшей переносимости и более гибкого подгонки используйте find:
if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
echo found
else
echo not found
fi
Явные -print -quit действия используются для find вместо неявного действия -print по умолчанию, чтобы найти strong > прекратит работу, как только найдет первый файл, соответствующий критериям поиска. Если количество файлов соответствует, это должно работать намного быстрее, чем echo glob*
или ls glob*
, а также избегает возможности перенапряжения расширенной командной строки (некоторые оболочки имеют ограничение длины 4 КБ).
Если find чувствует себя излишним, а количество файлов, которые могут соответствовать, невелико, используйте stat:
if stat -t glob* >/dev/null 2>&1
then
echo found
else
echo not found
fi
Ответ 3
#!/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
Ответ 4
мне нравится
exists() {
[ -e "$1" ]
}
if exists glob*; then
echo found
else
echo not found
fi
Это и удобочитаемо, и эффективно (если нет огромного количества файлов).
Основным недостатком является то, что он гораздо более тонкий, чем кажется, и я иногда вынужден добавить длинный комментарий.
Если есть совпадение, "glob*"
расширяется оболочкой, и все совпадения передаются в exists()
, которая проверяет первое и игнорирует остальные.
Если совпадений нет, "glob*"
передается в exist exists()
и также обнаруживается, что там не существует.
Изменение: может быть ложное срабатывание, см. Комментарий
Ответ 5
test -e имеет несчастливое предупреждение, что он считает, что сломанные символические ссылки не существуют. Поэтому вы можете также проверить их.
function globexists {
test -e "$1" -o -L "$1"
}
if globexists glob*; then
echo found
else
echo not found
fi
Ответ 6
Если у вас есть набор globfail, вы можете использовать этот сумасшедший (что вы действительно не должны)
shopt -s failglob # exit if * does not match
( : * ) && echo 0 || echo 1
или же
q=( * ) && echo 0 || echo 1
Ответ 7
У меня есть еще одно решение:
if [ "$(echo glob*)" != 'glob*' ]
Это хорошо работает для меня. Есть ли какие-то угловые случаи, которые я пропускаю?
Ответ 8
Чтобы немного упростить ответ MYYN, исходя из его идеи:
M=(*py)
if [ -e ${M[0]} ]; then
echo Found
else
echo Not Found
fi
Ответ 9
Основываясь на ответ flabdablet, для меня это выглядит как самый простой (не обязательно быстрый) - это просто использовать find, оставляя glob расширение на оболочке, например:
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
Ответ 10
В Bash вы можете glob к массиву; если glob не соответствует, ваш массив будет содержать одну запись, которая не соответствует существующему файлу:
#!/bin/bash
shellglob='*.sh'
scripts=($shellglob)
if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi
Примечание: если у вас установлен nullglob
, scripts
будет пустым массивом, и вы должны протестировать с помощью [ "${scripts[*]}" ]
или с помощью [ "${#scripts[*]}" != 0 ]
. Если вы пишете библиотеку, которая должна работать с или без nullglob
, вам нужно
if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]
Преимущество такого подхода заключается в том, что у вас есть список файлов, с которыми вы хотите работать, вместо того, чтобы повторять операцию glob.
Ответ 11
Эта мерзость, похоже, работает:
#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
echo "Glob matched"
else
echo "Glob did not match"
fi
Вероятно, требуется bash, а не sh.
Это работает, потому что опция nullglob заставляет glob оценивать пустую строку, если совпадений нет. Таким образом, любой непустой вывод из команды echo указывает на то, что glob что-то сопоставил.
Ответ 12
Я не видел этого ответа, поэтому подумал, что я его выложил:
set -- glob*
[ -f "$1" ] && echo "found [email protected]"
Ответ 13
#!/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
Ответ 14
if ls -d $glob > /dev/null 2>&1; then
echo Found.
else
echo Not found.
fi
Обратите внимание, что это может быть очень много времени, если существует много совпадений или доступ к файлам медленный.
Ответ 15
set -- glob*
if [ -f "$1" ]; then
echo "It matched"
fi
объяснение
Если для glob*
нет совпадения, то $1
будет содержать 'glob*'
. Тест -f "$1"
не будет верным, потому что файл glob*
не существует.
Почему это лучше, чем альтернативы
Это работает с sh и производными: ksh и bash. Он не создает никаких вложенных оболочек. Команды $(..)
и '...'
создают вложенную оболочку; они разворачивают процесс и поэтому работают медленнее, чем это решение.
Ответ 16
(ls glob* &>/dev/null && echo Files found) || echo No file found
Ответ 17
ls | grep -q "glob.*"
Не самое эффективное решение (если в каталоге есть тонна файлов в каталоге, это может быть медленным), но он прост, легко читается и также имеет то преимущество, что регулярные выражения более мощные, чем обычные шаблоны glob bash.
Ответ 18
[ 'ls glob* 2>/dev/null | head -n 1' ] && echo true