我的机器中有一个目录结构,如下所示
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感谢您的解释和答案。
答案 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
告诉它使用空字符完成名称。此空字符将允许我们将任何合法文件名传递给xargs
。 xargs
使用从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可能会更好地满足您的目的。
这些答案创建了当前工作目录中某个远程目录中的目录。这就是问题所要求的。
假设GNU find
和xargs
; 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
,然后创建它作为子目录,完全按照你的要求。
如果有的话,这会变得更容易:
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);
这只是从远程目录中读取名称,并在当前目录下创建这些目录。
这些答案创建了某个远程目录中当前工作目录中的目录。这与问题相反(但我自动做事的方式)。
假设GNU find
和xargs
; POSIX find
不支持-print0
或-printf
,POSIX xargs
也不支持-0
。
find . -maxdepth 1 -type d -print0 |
(cd /SomeWhere/Else; xargs -0 mkdir)
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