為什麼使用“*”萬用字元時文件移動/複製功能一次只能移動一個文件?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;} mv1 *.png
它只會移動
.png
它找到的第一個文件,而不是所有文件。如何使命令適用於與萬用字元匹配的所有文件?
mv1 *.png
首先將萬用字元模式擴展*.png
為匹配的文件名列表,然後將該文件名列表傳遞給函式。然後,在函式內部
$1
意味著:獲取函式的第一個參數,將其拆分到包含空格的位置,並用匹配的文件名列表替換任何包含萬用字元並匹配至少一個文件名的空格分隔部分。聽起來很複雜?確實如此,而且這種行為只是偶爾有用,而且經常有問題。這種拆分和匹配行為僅在$1
出現在雙引號之外時才會發生,因此修復很簡單:使用雙引號。除非您有充分的理由不這樣做,否則請**始終在變數替換周圍加上雙引號。**例如,如果目前目錄包含兩個文件
A* algorithm.png
和graph1.png
,則作為函式的第一個參數和第二個參數mv1 *.png
傳遞。然後拆分為和。模式匹配,並且不包含萬用字元。所以函式最終以參數、、和執行。如果您將功能更正為A* algorithm.png``graph1.png``$1``A*``algorithm.png``A*``A* algorithm.png``algorithm.png``mv``-n``A* algorithm.png``algorithm.png``targetdir``-v
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
然後它將正確移動第一個文件。
要處理所有參數,請告訴 shell 處理所有參數,而不僅僅是第一個。您可以使用
"$@"
表示傳遞給函式的完整參數列表。function mv1 { mv -n "$@" "targetdir" -v |wc -l ;}
這幾乎是正確的,但如果文件名恰好以字元開頭,它仍然會失敗
-
,因為mv
會將那個參數視為一個選項。通過--
tomv
告訴它“在此之後沒有更多選擇”。這是大多數命令都支持的非常常見的約定。function mv1 { mv -n -v -- "$@" "targetdir" |wc -l ;}
剩下的問題是,如果
mv
失敗,此函式將返回成功狀態,因為管道左側命令的退出狀態被忽略。在 bash(或 ksh)中,您可以使用set -o pipefail
來使管道失敗。請注意,設置此選項可能會導致在同一 shell 中執行的其他程式碼失敗,因此您應該在函式中本地設置它,這從 bash 4.4 開始是可能的。function mv1 { local - set -o pipefail mv -n -v -- "$@" "targetdir" | wc -l }
在早期版本中,設置
pipefail
會很脆弱,因此最好改為PIPESTATUS
顯式檢查。function mv1 { mv -n -v -- "$@" "targetdir" | wc -l ((!${PIPESTATUS[0] && !${PIPESTATUS[1]}})) }