移動與捕獲目錄的模式匹配的文件

移動與捕獲目錄的模式匹配的文件

考慮:

$ ls
foo  xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex

我試圖僅使用 mv 而不是 find 來實現相同的指令序列:

$ ls
foo  xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex

另外(編),

$ ls -F
foo/  xyfooz.tex*
$ mv -v *foo* foo

mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'

如何確保不會出現“stat”警告。換句話說,我想,我會改進模式以將來源限制為文件而不是目錄嗎?

GNU bash,4.3.48(1)-發布 (x86_64-pc-linux-gnu)

$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"

答案1

mv

cannot move 'foo' to a subdirectory of itself在這種情況下是無害的,您可以忽略它。我的意思是,mv儘管有警告,仍將移動其餘部分。然而,退出狀態不會是,這可能是腳本中的一個很大的障礙,如果不成功,0您想要中止或採取糾正操作。mv

這已經是在評論中說:您可以透過使用;mv重定向 stderr 來靜音2>/dev/null其他警告或錯誤也將被重定向。

我認為你不能輕易地“改進模式以將來源限制為文件而不是目錄”。您仍然可以採取與文字不符的方法foo。以下方法與檔案或目錄無關;只處理名稱,因為 glob 處理名稱;所以在某些情況下這可能還不夠。

glob*foo*符合四種析取類型的物件:

  1. foo本身
  2. foo 僅帶有前綴,例如bar-foo– 您可以透過以下方式匹配它們*?foo
  3. foo 帶有前綴和後綴,例如bar-foo-baz*?foo?*
  4. foo 僅帶有後綴,例如foo-bazfoo?*

要排除foo您只需要後三個(2-4),因此第一種方法可能是:

mv *?foo *?foo?* foo?* foo/

除非您nullglob設定了 shell 選項,否則任何模式都需要匹配某些內容,否則它將按字面意思傳遞mv。你不要這個。解決方案是預先執行以下命令:

shopt -s nullglob

此 shell 選項將使任何不匹配的 glob 擴展為空。我建議您也研究dotglob一下選擇。

請注意*?foo*匹配 (2-3)。類似*foo?*地匹配(3-4)。另外考慮--告訴mv大家以下所有參數都不是選項。這樣,類似的檔案--foo就不會被解釋為(未知)選項。這導致了兩個更好的命令:

mv -- *?foo* foo?* foo/

或者

mv -- *?foo *foo?* foo/

您選擇哪一個並不重要。只是不要使用mv *?foo* *foo?* foo/;它將傳遞(例如)bar-foo-baz兩次mv(即作為兩個單獨的參數),從而在工具第二次嘗試移動該物件時產生錯誤。

當根本沒有找到匹配項時,我的mv命令將自行退化mv foo/並拋出錯誤。您的原始mv命令(未nullglob設定)將獲取文字*foo*並拋出另一個錯誤。


控制台會話範例:

$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$ 

簡單的駭客攻擊

假設dummy不存在。

mv foo dummy
mv *foo* dummy/
mv dummy foo

在基於 inode 的檔案系統中重命名目錄應該非常快。包含已開啟檔案的程式foo/不應幹擾或破壞,因為重要的是 inode。但是,如果任何程式需要開啟foo/第一個和第三個之間的檔案(通過其路徑,包括) mv,它將失敗。可能會發生其他副作用(想像第三方程式foo/同時重新創建),這就是為什麼我稱這種方法為駭客。使用前請三思。


find無論如何

區分檔案和目錄是find.你所做的find基本上是正確的方法。你想要做什麼(以及我上面所做的)mv和 shell 通配符似乎......好吧,最多「不太正確」。這是你的命令:

find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;

find將為每個匹配的文件單獨運行mv,整個命令將執行得很差。由於這個原因,人們可能想切換到單一的mv.如果這是您的想法(並且您mv足夠find富有)那麼您應該考慮這種“非常正確”的方式:

find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +

find相對於指令和/或普通glob 的優點mv

  • 一個文件mv可能會處理多個文件;
  • 儘管如此,如果一個命令列無法處理太多文件,find則會運行額外的mv進程;
  • 如果沒有文件與模式匹配,mv則根本不會運行;
  • 感謝--這樣的文件--foo不會被解釋為mv;的(未知)選項

相關內容