[英]Read filenames with embedded whitespace into an array in a shell script
基本上,我正在使用find命令搜索存在於許多目錄中的多字文件,並且輸出存儲在變量vari上
vari = `find -name "multi word file.xml"
當我嘗試使用for循環來刪除文件時,
for file in ${vari[@]}
執行失敗說。
rm: cannot remove `/abc/xyz/multi':: No such file or directory
你們能幫我解決這種情況嗎?
bash
,主要是由於使用數組和進程替換( <(...)
) [1] ;那么POSIX兼容的解決方案會比較麻煩[2] ] ;還請注意,這是基於行的解決方案,因此它無法正確處理帶有嵌入式換行符的文件名,但這在實踐中很少見: # Read matches into array `vari` - safely: no word splitting, no
# globbing. The only caveat is that filenames with *embedded* newlines
# won't be handled correctly, but that's rarely a concern.
# bash 4+:
readarray -t vari < <(find . -name "multi word file.xml")
# bash 3:
IFS=$'\n' read -r -d '' -a vari < <(find . -name "multi word file.xml")
# Invoke `rm` with all array elements:
rm "${vari[@]}" # !! The double quotes are crucial.
find
直接執行刪除操作(這些解決方案還可以正確處理帶有嵌入式換行符的文件名): find . -name "multi word file.xml" -delete
# If your `find` implementation doesn't support `-delete`:
find . -name "multi word file.xml" -exec rm {} +
至於你嘗試了什么:
vari=`find -name "multi word file.xml"`
我已經刪除周圍的空間=
,這將導致一個語法錯誤) 不會創建一個數組; 這樣的命令替換將隨附命令的stdout輸出作為單個字符串返回(除去尾隨的換行符)。
( ... )
, 可以創建一個數組: vari=( `find -name "multi word file.xml"` )
, find
的輸出上執行單詞拆分 ,並且不能正確保留文件名和空格。 IFS=$'\\n'
此問題,以便僅在行邊界處進行拆分,但是生成的令牌仍會受到路徑名擴展(globbing)的影響 ,這會無意間更改文件路徑。 readarray
或read
是更簡單的選擇。 即使您確實設法將$vari
作為文件正確地收集到文件路徑中,也無法使用${vari[@]}
將該數組引用為${vari[@]}
-, 而不用雙引號 -會中斷,因為生成的字符串將再次受到字分割的影響 ,並且路徑名擴展(全局)。
"${vari[@]}"
[1]
使用進程替代而不是管道,以確保在當前外殼而不是子外殼中執行readarray
/ read
。
正如eckes在評論中指出的,如果您嘗試find ... | IFS=$'\\n' read ...
find ... | IFS=$'\\n' read ...
相反, read
將在subshell中運行,這意味着當命令返回時,它創建的變量將消失(超出范圍),並且以后無法使用。
[2]
POSIX Shell規范。 不支持數組也不支持進程替換(既不支持readarray
,也不支持-r
以外的任何read
選項); 您必須按以下方式實施逐行處理:
while IFS='
' read -r vari; do
pv vari
done <<EOF
$(find . -name "multi word file.xml")
EOF
請注意,鑒於$'\\n'
語法不可用, IFS='
和'
之間的要求實際換行符才能分配換行符。
以下是幾種方法:
# change the input field separator to a newline to ignore spaces
IFS=$'\n'
for file in $(find . -name '* *.xml'); do
ls "$file"
done
# pipe find result lines to a while loop
IFS=
find . -name '* *.xml' | while read -r file; do
ls "$file"
done
# feed the while loop with process substitution
IFS=
while read -r file; do
ls "$file"
done < <(find . -name '* *.xml')
如果您對結果滿意,則將ls
替換為rm
。
這些解決方案都是基於行的解決方案。 底部有一個測試環境,沒有已知的解決方案。
如前所述,可以使用以下經過測試的命令刪除文件:
$ find . -name "multi word file".xml -exec rm {} +
當路徑或文件名包含\\ n時,我沒有設法對變量使用rm命令。
測試環境:
$ mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47testdir" "")"
$ touch "multi word file".xml
$ mv *xml *testdir/
$ touch "2nd multi word file".xml ; mv *xml *testdir
$ ls -b
\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\ !"#$%&'testdir
$ ls -b *testdir
2nd\ multi\ word\ file.xml multi\ word\ file.xml
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.