考慮:
$ 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*
符合四種析取類型的物件:
foo
本身- foo 僅帶有前綴,例如
bar-foo
– 您可以透過以下方式匹配它們*?foo
- foo 帶有前綴和後綴,例如
bar-foo-baz
–*?foo?*
- foo 僅帶有後綴,例如
foo-baz
–foo?*
要排除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
;的(未知)選項