
在 bash 腳本中,我有以下變數:
file_name='this_is_the_hart_part.csv'
使用
var2=$(echo $file_name | sed -e 's/_{2}\(.*\)_{3}/\1/')
我想提取子字串“the”(變數 $file_name 中的底線數字 2 和 3 之間)。
但我得到的 $var2 等於 $file_name。我該如何更改我的 sed 命令?
答案1
支援的正規表示式類型sed
不允許與 進行非貪婪匹配*
。
您想要獲得第三個_
分隔欄位。這是最簡單的方法cut
:
cut -d '_' -f 3
或者,與awk
:
awk -F '_' '{ print $3 }'
或者,在 shell 中,連續刪除前兩個此類字段,然後修剪末尾:
str=${file_name#*_}
str=${str#*_}
str=${str%%_*}
"$str"
就是the
最後這個詞。使用最後一個變體可能是這三種方法中最快、最可靠的方法。
變數替換${variable#*_}
將產生一個字串,該字串的$variable
前導位直到並包括第一個下劃線被刪除。將${variable%%_*}
刪除從第一個底線到結尾的所有內容$variable
。這些是標準變數替換。
在檔案名上使用變數替換的好處是它可以處理包含換行符的檔案名,而 或或awk
都sed
不會cut
。一般來說,不要對檔案名稱使用面向行的文字編輯工具。
此外,您正在使用echo $file_name
.由於$file_name
未加引號,因此它將經歷單字分割(預設情況下,在也是空格、製表符和換行符的每個字元上$IFS
),並且產生的單字(如果它們包含檔案名稱通配符)將與當前目錄中的檔案名稱進行匹配通過外殼。檔案名稱中的反斜線也可能消失或產生不必要的效果(即使您引用副檔名)。當未加引號時,shellksh
也會對 的值進行大括號擴展。$file_name
答案2
首先要注意的sed
是文字預設情況下一次只處理一行的實用程序,而檔案名稱可以包含任何字元(包括換行符)甚至非字元(可以是非字元)文字)。
也,不加引號的變數有非常特殊的意義,你幾乎不想這樣做,這也是潛在非常危險。
另外,類似 Bourne 的 shell 中的變數賦值語法是:var=value
,而不是$var=value
。
echo
您可以使用以下命令將(或更好的是printf
)的整個輸出載入到sed
的模式空間:
printf '%s\n' "$filename" | sed -e :1 -e '$!{N;b1' -e '}'
然後,您可以新增程式碼來提取第二個和第三個之間的部分_
:
var2=$(
printf '%s\n' "$filename" |
sed -ne :1 -e '$!{N;b1' -e '}' -e 's/^\([^_]*_\)\{2\}\([^_]*\)_.*/\2/p'
)
非貪婪部分是透過使用[^_]*
(一系列非_
字元)來解決的,這與.*
保證我們不匹配過去的_
邊界相反(儘管在許多實現中它仍然會被非字元阻塞)。
在這種情況下,您可以改用 shell 參數擴充運算子:
case $filename in
(*_*_*_*) var2=${filename#*_*_}; var2=${var2%%_*};;
(*) var2=;;
esac
如果檔案名稱不是文字或您要提取的部分以換行符號結尾(並且也會更有效),則效果會更好。
有些 shell 喜歡zsh
或ksh93
有更高級的運算子:
zsh
:拆分
_
並取得第三個欄位:var2=${"${(@s:_:)filename}"[3]}
使用
${var/pattern/replacement}
和 反向引用(在這種情況下,您需要先驗證變數是否包含至少 3 個下劃線,否則不會有任何替換)。set -o extendedglob var2=${filename/(#b)*_*_(*)_*/$match[1]}
ksh93
:var2=${filename/*_*_@(*)_*/\1}
答案3
@Kusalananda 是對的,這sed
是錯誤的工具,你不能進行非貪婪匹配。但是您可以使用非貪婪匹配的解決方法:
[^_]*
將匹配任何不是的字符_
所以在你的情況下你可以這樣做:
printf '%s\n' "$file_name" | sed -e 's/^[^_]*_[^_]*_\([^_]*\).*$/\1/g'
但是...對於您的用例,您應該更好地使用其他工具...