透過 cp 從字串複製多個文件

透過 cp 從字串複製多個文件

我正在嘗試將多個檔案從 shellscript 複製到一個目錄。這些檔案包含各種「醜陋」字符,例如空格、括號等等。然而,當談到逃避這些問題時,我陷入了困境,bash並且cp似乎處理這些問題非常奇怪。

場景如下:
當從我的 shell 中發出此命令時,它就像一個魅力:
cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/

然而,當讀取要從字串複製的檔案時,它突然變得很奇怪: 導致
var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/

cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory

我相信這是因為我將變數作為字串給出,並cp相信它是一個文件,儘管它是多個文件。當發出相同的命令而不將變數放在引號(var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/)中時,我得到一個更奇怪的錯誤:
cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory

它似乎完全無視我的逃跑。我究竟做錯了什麼?

編輯:// 這好像是複製文件列表有一個答案xargs,但我仍然想知道為什麼 bash 在這裡表現得如此奇怪。

答案1

醫生,我這樣做的時候會很痛…

那就別那麼做!

說真的,為什麼要嘗試為單一變數分配一個以空格分隔的包含空格的檔案名稱清單?這是災難的根源。

回答您隱含的問題:我同意您顯示的錯誤訊息對於您所說的使用的命令沒有意義。

由於您沒有說明如何確定要複製哪些文件,因此很難知道哪種解決方案適合您,哪種解決方案不適合您。這是一種可能的方法:


f1="/somedir/a file.png"
f2="/somedir/另一個檔案.png"
cp "$f1" "$f2" "$f3" … /someotherdir

(您不需要/末尾的/someotherdir/,但它也沒有什麼壞處。)這受到以下事實的限制:您需要提前知道需要複製的最大文件數,並且您需要一些找出f將每個變數分配給哪個變數的方法。

這與您現在所做的很接近:

var="/somedir/a\ file.png /somedir/another\ file.png …"
迴聲“$var”|同時讀取 f1 f2 f3 …
    cp "$f1" "$f2" "$f3" … /someotherdir
完畢

read指令將$var在純空格處將字串分開,並去掉反斜杠,只留下 空格)。但您仍然需要提前知道需要複製的最大文件數。 (while即使該循環僅迭代一次,您也需要循環構造。)

也許這個已經很接近了:

文件列表=/tmp/my_filelist.$$
echo "/somedir/a file.png" >> "$filelist"
echo "/somedir/另一個檔案.png" >> "$filelist"
讀取檔案名稱時
    cp "$檔名" /someotherdir
完成<“$文件列表”

另一種變體是使用變數數組。您可以在手冊頁中了解如何執行此操作bash

答案2

當 bash 解析命令列時,它首先解釋引號、轉義符等,然後替換變數。如果變數值中有轉義符、引號等,它們永遠不會生效,因為當它們包含在命令中時,為時已晚,無法達到預期效果。

一般來說,處理此類問題的最佳方法是將檔案名稱儲存在陣列中(每個檔案名稱一個元素)而不是字串變數;然後使用"${array[@]}"來擴展數組,每個元素被視為一個單獨的“單字”:

files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/

更新:如果您需要動態建立文件列表,使用數組很容易:

files=() # Start with an empty list
for newfile in somethingorother; do
    files+=("$newfile")
done

答案3

如果您使用的是 bash,則可以雙引號檔案名稱以避免使用轉義字元:

cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp

#!/bin/bash在 shell 腳本中使用時,請使用它:

cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"

在您的情況下,您正在使用變數內的值,但您傳遞的變數是錯誤的。如果變數有空格,您也應該用雙引號將其括起來:

var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/

如果不使用雙引號,就像使用多個用空格分隔的變數一樣。

相關內容