
Я вижу это в BASH 4.3.48 (SLES12 SP4) и BASH 4.4.23 (OpenSUSE Leap 15.1) при попытке удалить несколько конечных пробелов из значения переменной:
~> xxx="-O -Wall "
~> echo "X${xxx%% }X" # (1)
X-O -Wall X
~> echo "X${xxx%% *}X"
X-OX
~> echo "X${xxx% }X"
X-O -Wall X
~> echo "X${xxx% *}X" # (2)
X-O -Wall X
~> echo "X${xxx%% \*}X"
X-O -Wall X
Я считаю, что либо , (1)
либо , (2)
должны выполнить эту работу.
В руководстве указано ${parameter%%word}
:
Удалить соответствующий шаблон суффикса. Слово расширяется для создания шаблона, как и при расширении имени пути. Если шаблон соответствует завершающей части расширенного значения параметра, то результатом расширения является расширенное значение параметра с удаленным самым коротким шаблоном соответствия (регистр ``%'') или самым длинным шаблоном соответствия (регистр ``%%'').
Поскольку это не работает так, как описано в документации (или как я понимаю документацию), я подозреваю, что это ошибка (несоответствующий суффикс (" -Wall
") удаляется в случае " %% *
") в BASH. Я прав?
решение1
В echo "X${xxx%% }X"
шаблоне это один пробел: . Самая длинная совпадающая часть для этого — это просто один пробел. Самая короткая совпадающая часть — это тоже просто один пробел.
Для чего-то большего вам понадобится оператор подстановки *
. Но это будет соответствовать чему угодно, удалив -Wall
. Подстановка Bash не поддерживает напрямую эквивалент регулярного выражения a*
. Вам понадобитсярасширенная подстановка:
$ shopt -s extglob
$ echo "X${xxx%%+( )}X"
X-O -WallX
решение2
Используйте удаление префикса внутри удаления суффикса:
$ xxx="-O -Wall "
$ echo "X${xxx%"${xxx##*[! ]}"}X"
X-O -WallX
- Удалить все до последнего символа, отличного от пробела, оставив только пробелы в конце.
- Используйте эти пробелы как шаблон для удаления суффикса.
- Расширение внутреннего параметра следует заключить в кавычки, чтобы предотвратить его интерпретацию как шаблона (выше это не обязательно, но может быть полезно в других случаях):
$ bash -c 'xxx="-O -Wall* "; echo "X${xxx%%"${xxx##*[! *]}"}X"'
X-O -WallX
$ bash -c 'xxx="-O -Wall* "; echo "X${xxx%%${xxx##*[! *]}}X"'
XX
Надуманный пример, но если внутреннее расширение не заключено в кавычки, то включенная в него звездочка будет рассматриваться внешним расширением как шаблон оболочки. В кавычках она становится буквальной звездочкой.
Наблюдаемое вами поведение не является ошибкой, это просто то, как работают простые шаблоны оболочки:
${xxx%% }
- один пробел — это один пробел
- Самое длинное появление одного пробела — один пробел
${xxx%% *}
- самое длинное появление одного пробела, за которым следует что-либо/ничего
- что угодно/ничего не будет включать
-Wall
${xxx% }
- самое короткое появление одного пробела — это один пробел
${xxx% *}
- самое короткое появление одного пробела, за которым следует что-либо/ничего, является одним пробелом
${xxx%% \*}
\*
представляет собой звездочку, экранированную обратной косой чертой, и будет интерпретироваться как буквальная звездочка- в переменной нет пробела, за которым следует звездочка, суффикс не удаляется
решение3
read
также может работать (при условии, что IFS
содержит «пробел»):
xxx="-O -Wall "
read -r xxx <<EOF
$xxx
EOF
echo "X${xxx}X"
Выход:
X-O -WallX
read
разбивает входные данные на поля в соответствии сIFS
IFS
по умолчанию это пробел/табуляция/новая строка, поэтому это удалит все начальные и конечные пробелы- Работает на первой строке переменной (может не подходить для многострочных переменных,
bash
можно использоватьread -d ''
)
решение4
Простое расширение параметра довольно ограничено в шаблонах, которые оно может сопоставить и удалить. Чтобы удалить несколько (повторяющихся) символов из конца строки, обычное решение заключается в том, чтобы сначала удалить все, что естьнетрассматриваемый символ ${xxx##*[! ]}
(все конечные пробелы). Затем, в качестве второго шага, удаление всего, что получается в результате этого расширения (все конечные пробелы) с конца даст вам то, что вы хотите (удаление конечных пробелов).
$ xxx="-O -Wall "
$ echo "<${xxx%"${xxx##*[! ]}"}>"
<-O -Wall>
В качестве альтернативы в bash можно использовать расширенную подстановку:
$ shopt -s extglob
$ echo "<${xxx%%+( )}>"
<-O -Wall>
Или, как альтернатива более высокого уровня, вы можете сопоставить то, что вам нужно, с помощью регулярного выражения:
$ regex='(.*[^ ]) +$';
$ [[ $xxx =~ $regex ]] && echo "<${BASH_REMATCH[1]}>" || echo "<$xxx>"
<-O -Wall>
Или, как сценарий:
#!/bin/bash
xxx=${1:-"-O -Wall "}
regex='(.*[^ ]) +$'
if [[ $xxx =~ $regex ]] # if there are trailing spaces
then
echo "<${BASH_REMATCH[1]}>" # Print the string without spaces
else
echo "<$xxx>" # if there are no trailing spaces.
fi