Проверьте, есть ли у шара какие-либо совпадения в bash - PullRequest
202 голосов
/ 30 мая 2010

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

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

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

Ответы [ 18 ]

155 голосов
/ 24 ноября 2010

Параметр оболочки nullglob - это действительно башизм.

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

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 действия, так что find завершится как только он найдет первый файл, соответствующий критериям поиска. В случае совпадения большого количества файлов это должно выполняться намного быстрее, чем echo glob* или ls glob*, и это также исключает возможность чрезмерного заполнения расширенной командной строки (некоторые оболочки имеют ограничение в 4 КБ).

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

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
145 голосов
/ 10 декабря 2015

Bash специальное решение:

compgen -G "<glob-pattern>"

Побег из паттерна, иначе он будет предварительно расширен на спички.

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

  • 1 для несоответствия,
  • 0 для «одного или более совпадений»

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

ОБНОВЛЕНИЕ : Запрошен пример использования.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi
22 голосов
/ 30 мая 2010
#!/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
18 голосов
/ 20 марта 2013

Мне нравится

exists() {
    [ -e "$1" ]
}

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

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

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

8 голосов
/ 28 июня 2014

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

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

или

q=( * ) && echo 0 || echo 1
7 голосов
/ 08 ноября 2013

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

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

if globexists glob*; then
    echo found
else
    echo not found
fi
5 голосов
/ 06 ноября 2015

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

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

Это хорошо работает для меня. Есть ли какие-то угловые случаи, по которым я скучаю?

4 голосов
/ 01 марта 2013

Основываясь на ответе 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
4 голосов
/ 30 мая 2010

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

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi
2 голосов
/ 03 мая 2018

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

#!/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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...