我有2个文件夹:folder_a&folder_b.在每个文件夹中都有一堆文件.我正在尝试sed将所有这些文件从这些文件夹中移出,并进入我当前的工作目录.
我的文件夹结构如下所示:
mytest:
a:
1.txt
2.txt
3.txt
b:
4.txt
5.txt
Run Code Online (Sandbox Code Playgroud)
我试图使用的命令是:
find . -type d ! -iname '*.*' # find all folders other than root
| sed -r 's/.*/&\/*/' # add '/*' to each of the arguments
| sed -r 'p;s/.*/./' # output: a/* . b/* .
| xargs -n 2 mv # should be creating two commands: 'mv a/* .' and 'mv b/* .'
Run Code Online (Sandbox Code Playgroud)
不幸的是我收到一个错误:
mv: cannot stat './aaa/*': No such file or directory
Run Code Online (Sandbox Code Playgroud)
当我尝试其他策略时(使用ls而不是mv),我也会得到相同的错误:
for dir in */; do
ls $dir;
done;
Run Code Online (Sandbox Code Playgroud)
即使我使用sed用'\'替换每个目录名中的空格,或用引号括起目录名,我也会得到同样的错误.
我不确定这两个例子是否与我对bash的误解有关,但它们似乎都表明我对bash如何将一个命令的输出转换为另一个命令的输入的无知.
任何人都可以对此有所了解吗?
更新:完全重写.
正如@EtanReisner和@melpomene所指出的那样,mv */* .或者更具体地说,mv a/* b/* .是最直接的解决方案,但是你说这部分是一个学习练习,所以答案的其余部分显示了一个有效find的解决方案并解释了问题.原始命令.
find的解决方案一般来说,如果可行的话,让find自己完成工作是最好和最有效的,而不需要额外的工具; find的-exec动作就像一个内置的xargs,{}表示手头的路径(带终结符\;)/ 所有路径(带+):
find . -type f -exec echo mv -t . {} +
Run Code Online (Sandbox Code Playgroud)
为了安全起见,他将只打印的mv 命令是将被执行; 删除echo实际执行它们.
这将执行所有匹配文件传递到的单个[1] mv命令,并将它们全部移动到当前目录.-t .
[1]如果生成的命令行太长(不太可能),它会被分成多个命令,就像使用xargs.
对文件(-type f)进行操作会绕过对globbing的需要,find然后会为您枚举所有文件(它也会绕过.显式排除的需要).
请注意,此解决方案适用于整个子树,而不仅仅是(立即)子目录.
考虑打开Bash 4的globstar选项和使用是很诱人的mv */** .,但这不会起作用,因为它也会尝试移动目录,而不仅仅是文件中的文件.
一个再警告-exec有+:它仅适用{}-占位符的所有路径-是令牌之前,立即在+.
既然你是在Linux上,我们可以通过指定目标文件夹满足这个条件mv与选项 -t 前的{}; 在基于BSD的系统(如OSX)上,你不能这样做,因为mv它不支持-t,所以你必须使用终结符\;,这意味着每个路径mv都会调用一次,这显然要慢得多.
正如@EtanReisner在注释中指出的那样,xargs调用没有(隐式)涉及shell的指定命令,因此globbing将不起作用 ; 您可以使用以下命令进行验证:
echo '*' | xargs echo # -> '*' - NO globbing
Run Code Online (Sandbox Code Playgroud)
如果我们将globbing问题放在一边,那么xargs使用嵌入空格(或其他shell元字符)的文件夹名称使命令正常工作将需要额外的工作:
find . -mindepth 1 -type d |
sed -r "s/.*/'&'\/* ./" | # -> '<input-path>'/* . (including single-quotes)
xargs -n 2 echo mv # NOTE: still won't work due to lack of globbing
Run Code Online (Sandbox Code Playgroud)
注意(组合)sed命令现在如何生成单个输出行'<input-path>'/* .,输入路径用嵌入的单引号括起来,即使它包含嵌入空格也需要将xargs其识别<input-path>为单个参数.
(如果你的文件名包含单引号,你必须做更多的工作;还要注意,由于现在对于一个给定的目录中的所有参数都在.单一的线,你可以使用xargs -L 1 ....)
还要注意如何-mindepth 1(仅使用子目录级别或更低级别的进程路径)跳过.自身的处理.
使globbing发生的唯一方法是让shell涉及:
find . -mindepth 1 -type d |
sed -r "s/.*/'&'\/* ./" | # -> '<input-path>'/* . (including single-quotes)
xargs -I {} sh -c 'echo mv {}' # works, but is inefficient
Run Code Online (Sandbox Code Playgroud)
注意使用xargs' -I选项将每个输入行视为自己的参数({}是输入的自选占位符).
sh -c 调用(默认)shell来执行生成的命令,在该命令处发生globbing.
但总的来说,这是非常低效的:
mv实用程序.将此与上述有效的find解决方案进行比较,该解决方案(通常)总共只创建2个进程.