
當嘗試從變數值中刪除多個尾隨空格時,我在 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}
:
刪除符合的後綴模式。這個單字被擴展以產生一個模式,就像路徑名擴展一樣。如果模式與參數擴展值的尾部部分匹配,則擴展的結果是具有最短匹配模式(“%”情況)或最長匹配模式(“%”)的參數擴展值。
由於它不像文件中那樣工作(或按照我對文件的理解),我懷疑這是BASH 中的一個錯誤(-Wall
在「」的情況下,不匹配的後綴(「」)被刪除)。%% *
我對嗎?
答案1
在 中echo "X${xxx%% }X"
,模式是單一空格:。最長的匹配部分就是:一個空格。最短的匹配部分也只是:一個空格。
對於更多的事情,您需要通配符運算符*
。但這將匹配任何內容,刪除-Wall
. Bash globbing 不支援直接擁有正規表示式的等效項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
這是一個人為的例子,但如果內部擴展沒有被引用,它包含的星號將被外部擴展視為 shell 模式。引用後,它就變成了字面上的星號。
您觀察到的行為不是錯誤,這只是簡單的 shell 模式的工作方式:
${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