В книге «Изучение оболочки Bash» О'Рейли, которую я читаю, приведен следующий код:
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
howmany=$1
shift
....
....
etc
Это использует
grep
утилиту поиска для проверки$1
соответствия соответствующему шаблону. Для этого мы предоставляем регулярное выражение^-[0-9][0-9]*$
grep, которое интерпретируется как «начальный тире, за которым следует цифра, необязательно за которой следует одна или несколько цифр». Если совпадение найдено, тоgrep
вернет совпадение, и тест будет истинным, в противном случаеgrep
не вернет ничего, и обработка перейдет кelif
тесту.Обратите внимание, что мы заключили регулярное выражение в одинарные кавычки, чтобы оболочка не интерпретировала символы $ и * и не передала их grep без изменений.
Тогда почему регулярное выражение не '^-[0-9]'
теряет своего смысла, как в одинарных кавычках? Обычно все, что находится внутри одинарных кавычек, теряет свой смысл.
Спасибо за помощь.
решение1
Хотя другие уже ответили на ваш конкретный вопрос, я добавлю, что
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
Это неправильный способ проверки соответствия строки регулярному выражению по нескольким причинам:
- Вы не можете использовать
echo
для произвольных данных - Если оставить расширение параметра без кавычек, как в
$1
приведенном выше примере, получится оператор split+glob. grep
не сопоставляет регулярное выражение со всем его вводом, а с каждой строкой его ввода. Так что он вернет true,foo\n-0\nbar
например.- Регулярное выражение может совпадать с нулевой длиной, поэтому в общем случае неправильно проверять,
grep
производит ли какой-либо вывод (обратите внимание, что подстановка команд удаляет конечные символы новой строки). Лучше использоватьgrep -q
и полагаться на статус выходаgrep
, а не на статус выхода[
, а также избегать подстановки команд. - Обратите внимание, что эту
grep
команду можно упростить доgrep -xE '-[0-9]+'
bash
, ksh93
и zsh
иметь выделенный оператор для (расширенного) сопоставления регулярных выражений. Чтобы использовать его переносимо и надежно во всех трех (и bash-3.1), синтаксис такой:
re='^-[0-9]+$'
if [[ $1 =~ $re ]]; then
echo matches
fi
yash
а zsh
также поддержка:
if [ "$1" '=~' '^-[0-9]+$' ]; then
echo matches
fi
Стандартная команда для выполнения сопоставления строк (базового) регулярного выражения expr
:
if expr " $1" : ' -[0-9]\{1,\}$' > /dev/null; then
echo matches
fi
Обратите внимание, что ^
(но не $
) подразумевается в expr
. Мы также используем этот начальный пробел, чтобы избежать проблем со значениями, $1
которые являются expr
операторами.
Также обратите внимание, что если регулярное выражение содержит \(...\)
, это повлияет на поведение expr
.
В целом, лучше использовать awk
другой стандартный/переносимый способ сделать то же самое (обратите внимание, что он awk
использует расширенные регулярные выражения):
if STRING=$1 RE='^-[0-9]+$' awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'; then
...
Или используйте функцию:
re_match() {
STRING=$1 RE=$2 awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'
}
if re_match "$1" '^-[0-9]+$'
В этом случае вы также можете добиться этого с помощью стандартной case
конструкции:
case $1 in
("" | *[!0-9-]* | [!-]* | - | ?*-*) ;;
(*) echo match;;
esac
Чтобы использовать grep
, вы можете использовать его с --null
опцией (где поддерживается, поскольку это не стандартная опция), чтобы указать ему работать с записями, разделенными NUL, вместо записей, разделенных новой строкой. Поскольку в большинстве оболочек $1
не может содержать NUL, это должно быть безопасно:
if printf %s "$1" | grep --null -xq '-[0-9]+$'; then
echo match
fi
решение2
Одинарные кавычки сообщают оболочке, что следует сохранить заключенные в них символы как есть,без какой-либо интерпретации. Строка в кавычках передается как есть grep
, без кавычек: при grep
поиске ее аргументов она видит
grep
и
^-[0-9][0-9]*$
и действует на этом основании. (ЧитатьКак запускаются программы(если вам интересно узнать о построении аргументов в Linux.)
bash
и grep
отличаются. То, как эта команда использует кавычки, гарантирует, что bash
не обрабатывает строку, но grep
обрабатывает.
решение3
Одинарные кавычки предотвращаютподстановка(позволяя bash
интерпретировать подстановочные знаки, такие как *
) и расширение переменной с помощью $
. По сути, вы говорите bash
"брать буквально эти символы и передавать их в grep
". Когда grep
видит их, он построен так, чтобы понимать регулярное выражение, поэтомузатемРегулярное выражение интерпретируется внутри grep
.
Более короткая версия: аргументы, заключенные в одинарные кавычки, позволяют избежать обработки в оболочке до того, как аргумент будет передан команде.
решение4
Он теряет свой смысл. grep
Использует почти те же шаблоны регулярных выражений, что и bash.