Проверьте, есть ли у шара какие-либо совпадения в 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