通过shell脚本复制目录结构

时间:2012-01-13 03:39:34

标签: regex bash shell

我的机器中有一个目录结构,如下所示

 bhagwat        durga    Sai Baba    vishnu sahastranam
  bhagwat geeta  hanuman  ramayan audio  shiv      vishnu

在上面的所有内容中都是名称中有空格的目录。 即如果你做ls -l,你会看到以下

drwx------ 1 deel deel        0 2011-09-12 21:34 bhagwat
drwx------ 1 deel deel     8192 2011-09-09 22:35 bhagwat geeta
drwx------ 1 deel deel     4096 2011-10-07 05:10 durga
drwx------ 1 deel deel        0 2011-10-29 09:23 hanuman
drwx------ 1 deel deel     8192 2011-09-30 22:48 ramayan audio
drwx------ 1 deel deel     4096 2011-09-18 12:16 Sai Baba
drwx------ 1 deel deel     4096 2011-09-26 19:19 shiv
drwx------ 1 deel deel     4096 2011-09-26 19:20 vishnu
drwx------ 1 deel deel     4096 2011-09-16 11:35 vishnu sahastranam

其中还有子文件夹。我不想复制文件或目录。 我想在其他位置或设备上创建如上所述的目录结构。这类似于源目录中存在的内容。 因此我正在编写一个脚本,如下所示

#!/bin/bash
for i in `ls /media/New\ Volume/bhajans`;do
echo $i 
done

现在这种方法的问题在于它会产生以下输出

bhagwat
bhagwat
geeta
durga
hanuman
ramayan
audio
Sai
Baba
shiv
vishnu
vishnu
sahastranam

如果你注意到输出,目录名中的空格被视为单独的目录。哪些是不正确的。那我怎么能摆脱这个问题。我想创建与源文件夹上相同的目录结构。< / p>

修改
我正在使用Ubuntu 11.04
更新
克隆的每个子目录都有一个名为script.sh的脚本,因此我不仅克隆了目录结构。我还将script.sh从父目录复制到克隆的目标。 Kevin和Jonathan感谢您的解释和答案。

6 个答案:

答案 0 :(得分:4)

将目录结构(递归地)从olddir克隆到newdir

find olddir -type d -printf "newdir/%P\0" | xargs -0 mkdir -p

如果您只想要第一级,请将-maxdepth 1添加到find

说明:

当然,

find以递归方式搜索目录并对其找到的文件进行操作。 olddir告诉它搜索名为“olddir”的目录。 -type d指定它只应对目录起作用。 -printf告诉它打印下面的模式:newdir是要克隆结构的目录。 %P打印文件名而不包含olddir部分,\0告诉它使用空字符完成名称。此空字符将允许我们将任何合法文件名传递给xargsxargs使用从stdin读取的内容作为参数执行您提供的命令。 -0告诉它使用空字节(\0)来确定参数(在我们的例子中是文件名)应该分开的位置。否则它将使用空格,我们知道我们不希望它这样做。 -p告诉mkdir制作父目录(如果它们尚不存在)。

好的,复制每个文件有点棘手,但我很确定这将适用于所有情况(所有情况下,至少安装了兼容的[gnu]查找)克隆结构后上面的命令:

$ find olddir -name script.sh -printf "%p\0" -printf "newdir/%P\0" | xargs -0L2 cp -n

第一个printf打印源(现有)文件(%p),第二个打印目标文件。两者都以\0结束,就像我们之前做的那样。 xargs的新参数-L2告诉xargs从输入中一次获取两(2)个参数(文件),并使每个命令只运行其中两个。因此printf语句和L2的顺序非常重要。 -n告诉cp不要覆盖目的地(如果它存在),以防万一我们犯了错误。

答案 1 :(得分:2)

尝试

find . -maxdepth 1 -type d | xargs -I X mkdir '/new/directory/X'

抱歉最初的错误。

答案 2 :(得分:1)

尝试这样的事情 -

while IFS=$\n read -r i; do 
    echo "$i"; 
done < <(ls -1 /media/New\ Volume/bhajans)

答案 3 :(得分:1)

如果它必须是一个shell脚本,那就有点乱了。如果它可以是从shell运行的脚本,那么Perl或Python可能会更好地满足您的目的。

答案对2

这些答案创建了当前工作目录中某个远程目录中的目录。这就是问题所要求的。

贝壳

假设GNU findxargs; POSIX find不支持-print0-printf,POSIX xargs也不支持-0

(cd "/media/New\ Volume/bhajans"; find . -maxdepth 1 -type d -print0) |
     xargs -0 mkdir -p

这可以确保find列出的名称尽可能简单(./name1等),但输出包括.mkdir .失败,因为目录存在。使用mkdir -p会阻止这是一个错误。

请注意-print0-print 0完全不同。前者是一个特殊命令,是-print find命令的变体。它不是用空格或换行符来终止名称,而是用ASCII NUL终止它。这是GNU的一个聪明的扩展。 256种可能性中只有两个字符不能出现在目录中的文件名中。其中一个是/,因为它被用作路径分隔符;另一个是ASCII NUL。通过使用ASCII NUL终止名称(在C代码中称为'\0';它具有字节值零),名称明确地彼此分开。但是,它确实需要期望该公约有效的计划;例如,xargs必须进行修改以识别它。

当您编写-print 0时,您告诉find打印名称,然后将另一个目录(称为0)添加到它应该搜索的列表中,并且它反对这一点。

当你写-printf 0时,你告诉他每次找到一个文件就打印一个0,所以它确实如此。它没有添加换行符或空格(你没有要求它),所以xargs看到一行包含00000000,然后创建它作为子目录,完全按照你的要求。

的Perl

如果有的话,这会变得更容易:

use strict;
use warnings;
my $otherdir = $ARGV[0];

die "$otherdir is not a directory" unless -d $otherdir;

opendir(my $dir, $otherdir) or die "failed to open directory $otherdir";
while (my $name = readdir($dir))
{
    next if $name eq "." || $name eq "..";
    mkdir $name if (-d $file);
}
closedir($dir);

这只是从远程目录中读取名称,并在当前目录下创建这些目录。

答案对1

这些答案创建了某个远程目录中当前工作目录中的目录。这与问题相反(但我自动做事的方式)。

贝壳

假设GNU findxargs; POSIX find不支持-print0-printf,POSIX xargs也不支持-0

find . -maxdepth 1 -type d -print0 |
    (cd /SomeWhere/Else; xargs -0 mkdir)

的Perl

use strict;
use warnings;
my $otherdir = $ARGV[0];

die "$otherdir is not a directory" unless -d $otherdir;

opendir(my $dir, ".") or die "failed to open current directory";
while (my $name = readdir($dir))
{
    mkdir "$otherdir/$name" if (-d $file);
}
closedir($dir);

未经过正式测试。

答案 4 :(得分:1)

正如您所指出的,问题显然是bash将目录名称中的空格视为分隔符,并分别迭代每个单词。 “正确”的解决方案是告诉它不要这样做,但如果没有这个,这是一个愚蠢的解决方法!

#!/bin/bash
for i in `ls -F | grep "/$" | sed s/\ /###/g`;do
    echo $i | sed s/"###"/\ /g
done

首先,ls -F | grep "/$"部分过滤你的ls结果只返回目录而不是文件(或符号链接)。然后,sed命令将目录名称中的空格转换为占位符字符串(“###”),这对于分隔符不会再出错。在循环内部,反向sed命令将它们转换回空格字符。

当然,您可以将“###”替换为您想要的任何非空格字符。

现在,使用它来创建目录:

#!/bin/bash
for i in `ls -F | grep "/$" | sed s/\ /###/g`;do
    tempname=`echo $i | sed s/"###"/\ /g`
    mkdir "otherlocation/$tempname"
done

只需将“otherlocation”替换为您要用于新目录的路径。

答案 5 :(得分:0)

你可以尝试

#!/bin/bash
for i in ls /media/New\ Volume/bhajans/* ;do
echo $i 
done