簡體   English   中英

將具有嵌入式空格的文件名讀取到Shell腳本中的數組中

[英]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)的影響 ,這會無意間更改文件路徑。
    • 雖然這可能也與外殼選項來解決,你現在有2個設置,您需要執行的時間提前,並恢復到原來的值; 因此,如上所述,使用readarrayread是更簡單的選擇。
  • 即使您確實設法將$vari作為文件正確地收集到文件路徑中,也無法使用${vari[@]}將該數組引用${vari[@]} -, 而不用雙引號 -會中斷,因為生成的字符串將再次受到字分割的影響 ,並且路徑名擴展(全局)。

    • 為了在不對其元素進行任何解釋的情況下安全地將數組擴展為其元素,請雙引號"${vari[@]}"

[1]

使用進程替代而不是管道,以確保在當前外殼而不是外殼中執行readarray / read

正如eckes在評論中指出的,如果您嘗試find ... | IFS=$'\\n' read ... find ... | IFS=$'\\n' read ...相反, read將在subshel​​l中運行,這意味着當命令返回時,它創建的變量將消失(超出范圍),並且以后無法使用。

[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM