一次创建多个子目录

一次创建多个子目录

假设我有一个目录/,它包含许多目录,,,/mydir并且每个目录都需要具有相似的结构。/hisdir/herdir

对于其中的每个目录/,都需要有一个目录doc和其中的一个文件doc1.txt

人们可能天真地认为他们可以执行

mkdir */doc
touch */doc/doc1.txt

但他们错了,因为通配符不是这样工作的。

有没有一种方法可以做到这一点,而不必只在一个示例中创建一次结构,然后cp将其传递给其他示例?

而且,如果没有,是否有一种方法可以在不覆盖任何现有文件的情况下执行上述解决方法(假设mydir已经包含带有我想要保留的一些数据的结构)?

编辑:如果可能的话,我还想避免使用脚本。

答案1

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

但是,实际上,简单循环的优点是简单——而且不需要更多的打字!

答案2

zsh

dirs=(*(/))
mkdir -- $^dirs/doc
touch -- $^dirs/doc/doc1.txt

(/)是一个通配限定符,/表示仅选择目录。

$^array(让人想起 的rc运算^符)是在数组上打开一个括号类型的扩展,所以$^array/doc就像{elt1,elt2,elt3}/doc(其中elt1elt2elt3是数组的元素)。

还可以这样做:

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 shell 中的替代运算符。对于${array[@]/pattern/replace},它会应用于数组的每个元素。%这意味着在最后

各种考虑:

dirs=(*/)[ -L "$file" ]包括目录和目录的符号链接(除了在循环中使用之外,没有办法排除符号链接),而dir=(*(/))(zsh扩展)仅包括目录(dir=(*(-/))包括目录的符号链接而不添加尾部斜杠)。

它们排除隐藏目录。每个 shell 都有包含隐藏文件的特定选项)。

如果当前目录可供其他人写入,则可能会遇到安全问题。因为人们可以在那里创建一个符号链接,导致您在不想要的地方创建目录或文件。即使使用不考虑符号链接的解决方案,仍然存在竞争条件,因为人们可能能够用 和 之间的符号链接替换dirs=(*/)目录mkdir...

答案3

for d in /*/
do  [ -d "$d" ] || break
    f=$d/docs/doc1.txt    
    mkdir -p -- "${f%/*}"
    touch -- "$f"
done

我认为它符合你的目标。也许我错过了一些东西。

否则你可以:

set -- /*/
[ -d "$1" ] && 
printf 'd=$%d
    mkdir -p -- "$d/docs" && 
    touch -- "$d/docs/doc1.txt"
' $(seq "$#") | sh -s -- "$@"

或者:

set -- /*/
while [ -d "$1" ] 
do  mkdir -p -- "$1"/docs
    touch -- "$1"/docs/doc1.txt
shift ; done

答案4

好的,让我们来实现吧短的

ls -1|xargs -i% bash -c 'mkdir %/doc;>>%/doc/doc1.txt'


- 我们在像你一样的目录上起始目录

  • ls -11列表herdir等,每行一个(没有其他内容)

  • xarg使用脚本参数运行 shell,用目录替换 %

  • 对于每个目录:

    • bash将它的一个字符串参数作为 shell 脚本运行,包含两个命令

      • mkdir %/doc创建目录
      • >>%/doc/doc1.txt创建空文件2


$ ls
$ mkdir mydir hisdir herdir
$ ls                       
herdir  hisdir  mydir
$ ls -1|xargs -i% bash -c 'mkdir %/doc;>>%/doc/doc1.txt'
$ find
.
./herdir
./herdir/doc
./herdir/doc/doc1.txt
./hisdir
./hisdir/doc
./hisdir/doc/doc1.txt
./mydir
./mydir/doc
./mydir/doc/doc1.txt



1)艺术自由;寻找 。 -最大深度 1 -类型 d

2)>>file这里创建一个空文件,与touch类似。具体可以bash看看man bash,使用键 / 搜索“输出重定向”

相关内容