一次創建多個子目錄
假設我有一個 directory
/
,它包含許多目錄/mydir
,/hisdir
,/herdir
,並且每個目錄都需要具有相似的結構。對於其中的每個目錄
/
,都需要有一個目錄doc
和一個文件doc1.txt
。人們可能天真地認為他們可以執行
mkdir */doc touch */doc/doc1.txt
但他們會錯的,因為萬用字元不是那樣工作的。
有沒有辦法做到這一點,而不僅僅是在一個例子中製作一次結構然後
cp
將它傳遞給其他人?而且,如果沒有,有沒有辦法在不覆蓋任何現有文件的情況下執行上述解決方法(假設
mydir
已經包含具有我想要保留的一些數據的結構)?編輯:如果可能的話,我也想避免使用腳本。
與
zsh
:dirs=(*(/)) mkdir -- $^dirs/doc touch -- $^dirs/doc/doc1.txt
(/)
是一個萬用字元,/
表示只選擇目錄。
$^array
(讓人想起rc
’s^
運算符)是在數組上打開類似大括號的擴展類型,$^array/doc
就像{elt1,elt2,elt3}/doc
(其中elt1
,elt2
,elt3
是數組的元素)。還可以這樣做:
mkdir -- *(/e:REPLY+=/doc:) touch -- */doc(/e:REPLY+=/doc1.txt:)
e
另一個全域限定符在哪裡執行文件上的某些給定程式碼以進行選擇。與
rc
/es
/akanga
:dirs = */ mkdir -- $dirs^doc touch -- $dirs^doc/doc1.txt
那就是使用
^
類似於增強連接運算符的運算符。
rc
不支持萬用字元(這是 zsh 獨有的功能)。*/
擴展到所有目錄和目錄的符號連結,並/
附加。與
tcsh
:set dirs = */ mkdir -- $dirs:gs:/:/doc::q touch -- $dirs:gs:/:/doc/doc1.txt::q
是
:x
歷史修飾符,也可以應用於變數擴展。:gs
用於全域替代。:q
引用單詞以避免某些字元出現問題。使用
zsh
或bash
:dirs=(*/) mkdir -- "${dirs[@]/%/doc}" touch -- "${dirs[@]/%/doc/doc1.txt}"
${var/pattern/replace}
是 Korn-like shell 中的替代運算符。使用${array[@]/pattern/replace}
,它將應用於數組的每個元素。%
有最後的意思。各種考慮:
dirs=(*/)``[ -L "$file" ]
包括目錄和指向目錄的符號連結(除了在循環中使用之外,沒有辦法排除符號連結),而dir=(*(/))
(zsh 副檔名)僅包括目錄(dir=(*(-/))
包括指向目錄的符號連結而不添加尾部斜杠)。他們排除隱藏的目錄。每個 shell 都有包含隱藏文件的特定選項)。
如果目前目錄可由其他人寫入,則可能存在安全問題。因為可以在那裡創建一個符號連結,以使您在不希望的地方創建目錄或文件。即使使用不考慮符號連結的解決方案,仍然存在競爭條件,因為人們可能能夠用
dirs=(*/)
和之間的符號連結替換目錄mkdir...
。
在 bash 中,使用類似以下行的內容:
mkdir -p {mydir,hisdir,herdir}/doc touch {mydir,hisdir,herdir}/doc/doc1.txt
該
{...}
語法稱為“大括號擴展”,與路徑名擴展不同,文件名必須存在,生成的結果不需要匹配已經存在的任何內容。並且該-p
方法根據需要創建路徑的所有嵌套組件 - 否則,當 mkdir 嘗試在父母之前創建最終的“doc”目錄時,您將收到錯誤消息。(查看 bash 手冊頁中的範例;像這樣創建子目錄正是常見的案例。)
如果 mydir、hisdor 和 herdir 已經存在並且您不想重新鍵入它們,Stéphane Chazelas 的解決方案可能是最聰明的,但除非您一直這樣做,否則聰明並不總是最好的——我永遠記不起bash 數組擴展的東西,我敢打賭許多初級系統管理員不會認出它。在這種情況下,我想我會推荐一個循環或
find
,像這樣:find . -maxdepth 1 -mindepth 1 -type d \ -execdir mkdir {}/doc \; -execdir touch {}/doc/doc1.txt \;
但是,實際上,簡單循環的優點是簡單明了——而且不需要更多的輸入!