Это часть сценария bash с указанными номерами строк. Я понимаю, как работает getopts в bash, но не могу понять строку 116. if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]; then
часть. Ранее в сценарии было
#!/usr/bin/env bash
FS_OPTIONS=("ubuntu" "busybox")
while getopts "hsf:" opt; do
case $opt in
f)
if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]; then
echo "Unsupported filesystem $OPTARG"
echo "Use ubuntu/busybox"
exit -1
else
echo "ok!"
export FILESYSTEM=$OPTARG
fi
;;
esac
done
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubuntu
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f busybox
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
Unsupported filesystem ubunt.
Use ubuntu/busybox
Я изменил строку условия на if [[ ! " ${FS_OPTIONS[@]} " =~ $OPTARG ]]; then
и вижу, что правая часть считается регулярным выражением. Теперь я вижу
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
ok!
Это связано с тем, что " " был удален, а аргумент был принят как регулярное выражение.
В руководстве по bash написано
Доступен дополнительный бинарный оператор =~ с таким же приоритетом, как == и !=. Когда он используется, строка справа от оператора считается расширенным регулярным выражением и соответствующим образом сопоставляется (как в регулярном выражении (3)). Возвращаемое значение равно 0, если строка соответствует шаблону, и 1 в противном случае.
Это было задолго до моего вопроса. Мой первый вопрос: из того, что я заметил выше, когда шаблон совпадает, =~ возвращает 1. (до этого было!), в отличие от руководства. Я что-то пропустил?
Мои вторые вопросы: каковы варианты использования оригинала if [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]; then
? использовать регулярное выражение? Поскольку у него есть " ", он примет аргумент как строку (не принимая его как регулярное выражение). Есть ли какая-либо польза для =~ с " "?
Тем не менее, обратите внимание, что вопросы «почему» о языковом дизайне здесь обычно не по теме; см. meta.stackexchange.com/a/170415/144918
@KamilCuk Например, когда я дал -f ubuntu
, это показывает, что оно совпало (напечатано ok
выше). Это означает, что первая часть if ( ! ... =~ ... )
не была взята, а это означает, что часть =~ вернула 1. Поэтому я дал шаблон соответствия, но =~ вернул 1. (в руководстве сказано, что при совпадении возвращается 0). Я что-то пропустил?
(Кстати, exit -1
обычно не имеет смысла в оболочке: статус выхода UNIX представляет собой целое число без знака, поэтому отрицательные числа всегда преобразуются в положительные).
... кроме того, [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]
не лучшая форма. Следует использовать [*]
вместо [@]
в контексте, где строка не может расширяться до нескольких слов.
Еще одно замечание: переменные с заглавными буквами используются для имен, определенных спецификацией POSIX или иным образом изменяющих поведение оболочки и других инструментов, определенных POSIX; тогда как имена, содержащие хотя бы один символ нижнего регистра, зарезервированы для использования приложением, и поэтому вы должны использовать их для новых переменных, которые вы определяете сами. См. соответствующую спецификацию на pubs.opengroup.org/onlinepubs/9699919799/basedefs/…, имея в виду, что переменные оболочки и среды имеют общее пространство имен (поскольку установка переменной оболочки перезаписывает любую переменную среды с таким же именем).
Разбивка того, что сделано [[ ! " ${FS_OPTIONS[@]} " =~ " $OPTARG " ]]
:
" ${FS_OPTIONS[@]} "
в этом контексте является менее четким эквивалентом " ${FS_OPTIONS[*]} "
(поскольку обычное различие между ними будет иметь [@]
расширение до нескольких слов, что не является законным в этом контексте). Таким образом, действуя как ${FS_OPTIONS[*]}
, мы расширяемся до полного списка слов в FS_OPTIONS
с добавлением разделителя (первый символ в IFS, по умолчанию один пробел). Также обратите внимание, что мы добавляем пробелы в начале и в конце (чтобы наш OPTARG
можно было сопоставить в этих позициях, а не только в середине)." $OPTARG "
дополняет искомую строку пробелами с обеих сторон, поэтому мы не можем сопоставить подстроку.$OPTARG
— с добавленными пробелами в начале и конце — где-либо в полном расширенном списке, сгенерированном ${FS_OPTIONS[*]}
.!
инвертирует логический статус выхода остальной части выражения (изменение 0 на 1 или 1 на 0).Строка в кавычках в RHS действует как обычный незакрепленный поиск подстроки. То есть [[ $foo = "bar" ]]
истинно, только если переменная foo
расширяется точно до bar
, но [[ $foo =~ "bar" ]]
истинно, если переменная foo
расширяется и содержит bar
в любом месте своего содержимого. Это полезная семантика сама по себе.
Цитирование определяется посимвольно, а не для всей строки. В качестве примера того, как это можно использовать:
regex_pre='([[:space:]]|^)'
literal_string='** match this exactly **'
regex_post='([[:space:]]|$)'
[[ $foo =~ ${regex_pre}"${literal_string}"${regex_post} ]]
# | | |
# | | \-> unquoted: acts like a regex
# | \-> quoted: acts like a literal string
# \-> unquoted: acts like a regex
...соответствует ** match this exactly **
только тогда, когда он находится в начале строки или непосредственно перед пробелом, а также либо в конце, либо сразу после пробела, без необходимости писать регулярное выражение для литеральной строки между ними и без необходимости делать трюк с пробелами, показанный в вопросе.
вау, большое спасибо за любезное объяснение. последнюю часть (regex_pre~ regex_post) я не могу сейчас, но я проверю это позже (просто потому, что здесь так поздно).
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubuntu
ok!
ckim@chan-ubuntu:~/testbash$ test3.sh -f ubunt.
Unsupported filesystem ubunt.
Use ubuntu/busybox
В руководстве bash сказано (в обсуждении [[...]]):
Любая часть шаблона может быть заключена в кавычки, чтобы часть в кавычках соответствовала строке.
Итак, чтобы обрабатывать ввод как регулярное выражение, а также добавить литеральные пробелы на концах, заключите пробелы в кавычки, но оставьте переменную без кавычек:
[[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" " ]]
# .........................^^^-------^^^
Демонстрация:
$ OPTARG=ubuntu
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" " ]] && echo Y || echo N
Y
$ OPTARG=ubunt.
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" " ]] && echo Y || echo N
Y
$ OPTARG=ubunt
$ [[ " ${FS_OPTIONS[*]} " =~ " "$OPTARG" " ]] && echo Y || echo N
N
(it has ! before) as opposed to the manual.
Как это "против"? Есть! expression True if expression is false
.