為什麼使用「*」通配符時檔案移動/複製功能一次只能移動一個檔案?

為什麼使用「*」通配符時檔案移動/複製功能一次只能移動一個檔案?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
mv1 *.png

它只會移動.png找到的第一個文件,而不是所有文件。

如何使該命令應用於與通配符匹配的所有檔案?

答案1

mv1 *.png首先將通配符模式擴展*.png為匹配檔案名稱列表,然後將該檔案名稱列表傳遞給函數。

然後,在函數內部$1意味著:將第一個參數傳遞給函數,在包含空格的地方將其拆分,並替換任何包含通配符的空格分隔部分,並透過匹配檔案名稱清單來匹配至少一個檔案名稱。聽起來很複雜?確實如此,而且這種行為只是偶爾有用,而且經常會出現問題。這種拆分和匹配行為僅發生$1在雙引號之外,因此修復很簡單:使用雙引號。始終在變數替換兩邊加上雙引號除非你有充分的理由不這麼做。

例如,如果目前目錄包含兩個檔案A* algorithm.pnggraph1.png,則作為第一個參數mv1 *.png傳遞A* algorithm.png給函數,並graph1.png作為第二個參數傳遞。然後$1分為A*algorithm.png。此模式A*符合A* algorithm.png, 且不algorithm.png包含通配符。因此函數最終mv以參數-nA* algorithm.pngalgorithm.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]}}))
}

答案2

$1是函數的第一個參數,這裡是第一個符合的檔案*.png。我想這"$@"就是您想要使用的而不是$1.

答案3

你必須使用mv1 \*.png.

與函數互動時,Linux 終端不會直接將星號傳遞給命令,而是選擇第一個符合的參數並將其傳遞給命令。

為了讓星號直接通過,需要使用反斜線轉義星號。

相關內容