Linux 僅在給定的一組文件中搜尋字元串
一個目錄中有幾個文件。我正在嘗試搜尋並找到所有以給定字元串結尾的字元串。我不想搜尋目錄中存在的所有文件,而只想搜尋給定的一組文件名。最後,輸出應該是每個文件名以及在該文件中找到的帶有分號分隔符的字元串。
簡化的測試案例是:目錄下有5個文件:
file.a.txt file.b.txt file.c.txt file.d.txt file.e.txt
還有一個名為的文件
searchFiles.txt
,其中包含上面列表中的前 3 個文件名。所以我只想在searchFiles.txt
.我努力了:
for i in $(cat searchFiles.txt); do grep -o '[^ ]*_XYZ' /dev/null $i ; done | awk -F: '{a[$1]=a[$1]";"$2;} END{for (x in a) print x ":" substr(a[x],2);}'
但輸出說
: No such file or directory : No such file or directory file.c.txt:FOUND1_XYZ;FOUND2_XYZ
所以不知何故,它只能在 searchFiles.txt 中給出的最後一個文件名中搜尋,但找不到其他初始文件,因此拋出錯誤“沒有這樣的文件或目錄”
我期待的輸出是:
file.a.txt:FOUNDSTR_XYZ file.b.txt:FOUNDSTR1_XYZ;FOUNDSTR2_XYZ;FOUNDSTR3_XYZ file.c.txt:FOUND1_XYZ;FOUND2_XYZ
我還試圖找出“find”命令的“-name”標誌在這裡是否有幫助,但不能完全了解如何準確地從 searchFiles.txt 提供文件列表。下面的嘗試徒勞無功。
find . -type f -name `cat searchFiles.txt` -exec grep -o '[^ ]*_XYZ' /dev/null {} \;
還:
- 一個目錄中最多可以有幾千個文件,searchFiles.txt 中的搜尋文件名可以是幾百個文件名。
- 文件名可以是任何東西,並且不遵循任何模式。
- searchFiles.txt 中提供的文件名可以是部分名稱,例如 a.txt 而不是 file.a.txt,這意味著文件名“file”的初始靜態部分。searchFiles.txt 中可能存在也可能不存在。
- 最好尋找單行命令而不是 shell 腳本
請問有什麼幫助嗎?
我假設您已經修復了評論中討論的 DOS 樣式的行尾,並且
searchFiles.txt
實際上不包含空行。的
-name
測試find
只採用一種文件名模式。模式可能包含 shell glob 字元,但應保護這些字元免受 shell 過早生成文件名。您可以使用邏輯 OR 連接多個此類測試,-o
但您需要注意運算符的優先級。如果您的 shell 支持數組,那麼您可能能夠做到的一種方法如下(我在
bash
這裡使用,但在其他 shell 中應該可以使用類似的過程):files=( -false ) while IFS= read -r f || [ -n "$f" ]; do files+=( -o -name "*$f"); done < searchFiles.txt
這應該導致
${files[@]}
擴展到交替-false -o -name *file.a.txt -o -name *file.b.txt -o -name *file.c.txt -o -name *file.d.txt -o -name *file.e.txt
然後你可以在你的
find
命令中使用它find . \( "${files[@]}" \) -exec grep -Ho '[^ ]*_XYZ' {} +
(我省略了虛擬文件
/dev/null
以支持添加-H
選項)。**如果其中的文件數量searchFiles.txt
太大,這種方法可能會由於ARG_MAX
限製而失敗。**您可以通過拆分searchFiles.txt
為多個較小的文件來解決此限制。
您應該能夠使用 GNU 完成所有操作
awk
,例如:find . -type f -print0 | gawk ' step == 1 {files[$0]; next} # record file names in "files" array step == 2 { # determine which files to look into (added to ARGV array for # processing in step 3) if ($NF in files) ARGV[ARGC++] = $0; next } NF { # record all matches (here in fields matched by FPAT) $1 = $1 # force a rebuild of $0 joining fields with OFS matches[FILENAME] = matches[FILENAME] \ (matches[FILENAME] == "" ? "" : OFS) \ $0 } END { for (file in matches) print file ": " matches[file] }' step=1 searchFiles.txt \ step=2 RS='\0' FS=/ - \ step=3 RS='\n' FPAT='[^ ]*_XYZ' OFS=';'
上面,文件名與儲存在
searchFiles.txt
. 如果該文件的行是後綴列表,則可以建構一個正則表達式而不是關聯數組:find . -type f -print0 | gawk ' step == 1 { gsub(/[][^$*()+{}?\\.|]/, "\\\\&") # escape regexp operators regex = regex sep $0; sep = "|" next } step == 2 { # determine which files to look into (added to ARGV array for # processing in step 3) if ($NF ~ ("(" regex ")$")) ARGV[ARGC++] = $0; next } NF { # record all matches (here in fields matched by FPAT) $1 = $1 # force a rebuild of $0 joining fields with OFS matches[FILENAME] = matches[FILENAME] \ (matches[FILENAME] == "" ? "" : OFS) \ $0 } END { for (file in matches) print file ": " matches[file] }' step=1 searchFiles.txt \ step=2 RS='\0' FS=/ - \ step=3 RS='\n' FPAT='[^ ]*_XYZ' OFS=';'
如果需要混淆,可以放在一行:
find . -type f -print0|gawk '!s{gsub(/[][^$*()+{}?\\.|]/,"\\\\&");r=r p $0;p="|";next};s==2{if($NF~("("r")$"))ARGV[ARGC++]=$0;next};NF{$1=$1;m[FILENAME]=m[FILENAME](m[FILENAME]==""?"":OFS)$0};END{for(f in m)print f":"m[f]}' searchFiles.txt s=2 RS=\\0 FS=/ - s=3 RS=\\n FPAT='[^ ]*_XYZ' OFS=\;
除了必須是區域設置中的有效字元之外,它們不會假設文件名和內容可能包含哪些字元。後綴不能有換行符,但這是由
searchFiles.txt
.