使用正則表達式查找參數中給出的副檔名的文件
假設一個有以下命令
search /home/user proc .h .c .txt ...
我正在使用
find
命令建構一個腳本,以獲取所有以給定名稱開頭並以給定副檔名之一結尾的文件。我已經設法使用循環來建構它:
directory=$1 fileName=$2 fileExtensions="" for arg in "$@" do #skipping first and second argument if [ $arg = $1 -o $arg = $2 ]; then continue; fi fileExtensions+="${arg//.}\|" done #removing the last '|', otherwise regex parser error occurs fileExtensions=${fileExtensions::-1} find $directory -name "$fileName*" -regex ".*\.\($fileExtensions)"
有沒有更優雅的方法可以使用正則表達式來實現這一點?
感謝您的幫助!
該腳本可以簡化為:
directory=$1 fileName=$2 shift 2 a="$*" b="${*#.}" (( ${#a} - ${#b} - $# )) && echo "some extension(s) is(are) missing a leading dot." >&2 fileExtensions="$(IFS=\|; echo "${*#.}")" find "$directory" -name "$fileName*" -regextype posix-egrep -regex ".*\.($fileExtensions)$"
預設情況下,find 接受 emacs 類型的正則表達式,要使用該類型需要幾個反斜杠,例如
\|
. 這可以通過使用不同類型的正則表達式來避免(如上所述)。Where
${*#.}
刪除前導點(如果存在)並將所有剩餘的“位置參數”與 IFS 的第一個字元的值連接起來,該值設置為|
用於執行子 shell。只需一個變數賦值和一個“參數擴展”,這就是所有需要的。
EDIT用於檢查參數列表中提供的所有擴展是否都以點開頭
。
(( ${#a} - ${#b} - $# ))
連接的所有參數的字元數 (
${#a}
) (a=$*
)應等於連接的所有參數
的字元數 (
${#b}
)(刪除一個前導點) (b=${*#.}
)加上
參數的數量 (
$#
)。${#a} == ${#b} + $#
當且僅當所有參數都有一個前導點。
作為算術測試:
(( ${#a} - (${#b} + $#) ))
或者還有:
(( ${#a} - ${#b} - $# )) && echo "missing leading dot(s)."
編輯二
命令行參數中給出的擴展列表在此處處理:
fileExtensions="$(IFS=\|; echo "${*#.}")"
這是它的工作原理,由內而外:
$* # This generates a string of all positional arguments using IFS.
來自
LESS=+'/Special Parameters' man bash
:那是, ” $ *" is equivalent to " $ 1c$2c…",其中 c 是 IFS 變數值的第一個字元。
然後我們使用“參數擴展”來切割每個位置參數的前面:
${parameter#word} # used as ${*# } above.
來自
LESS=+/'parameter#word' man bash
:如果參數是@或*,模式移除操作將依次應用於每個位置參數,擴展是結果列表。
前
word
一個擴展中的 dot 設置為 dot.
,從而刪除了所有位置參數前面的點。由於此時的 IFS 以一個
|
字元開頭,該字元用於建構一個字元串,該字元串|
作為參數列表的分隔符,該參數列表在前面帶有一個點。該字元串被提供給命令 echo 以使其列印。
但是在執行 echo 命令之前,變數
$IFS
被設置為 a|
。它包含在命令執行
$(…)
中(創建一個子 shell,它在結束時會忘記對 IFS 的更改)。然後我們將字元串分配給一個變數:
fileExtensions="$(IFS=\|; echo "${*#.}")"
簡而言之:轉換
.c .h .txt
為c|h|txt
.