Bash

一次創建多個子目錄

  • June 26, 2019

假設我有一個 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 引用單詞以避免某些字元出現問題。

使用zshbash

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 \;

但是,實際上,簡單循環的優點是簡單明了——而且不需要更多的輸入!

引用自:https://unix.stackexchange.com/questions/148382