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