
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"
、パターンは 1 つのスペースです: 。その最長の一致部分は、まさに 1 つのスペースです。最短の一致部分も、まさに 1 つのスペースです。
それ以上のことをするには、グロブ演算子 が必要です*
。しかし、これは を除けば何にでもマッチします-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%% }
- 1つのスペースは1つのスペースです
- 単一のスペースの最長出現は単一のスペースである
${xxx%% *}
- 単一のスペースの後に何か/何も続かない最長の出現
- 何も含まれません
-Wall
${xxx% }
- 単一のスペースの最短出現は単一のスペースです
${xxx% *}
- 1つのスペースの後に何か/何も続かない最短の出現は1つのスペースです
${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##*[! ]}
(末尾のスペースすべて)を削除します。次に、2 番目の手順として、その拡張の結果のすべて(末尾のスペースすべて)を末尾から削除すると、必要な結果(末尾のスペースの削除)が得られます。
$ 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