我试图做以下事情,
find . -name "*.dat" | get the basename of file | move filename returned by first command to basename returned by second command
举个具体的例子,我想./mydir1/dir2/file1.dat
改名为file1.dat
.
我可以用管道来做吗?如果是,如何将第一个命令的输出存储在第一个管道的变量中并在第二个管道中使用它。我希望我正确使用了术语“管道”。
答案1
您不需要管道,因为 find 命令本身就能够做到这一点:
find . -name "*.dat" -exec mv -t . {} \;
请注意,这有点低效,因为.dat
当前目录中已有的文件也会被找到并移动。
答案2
您无法真正影响管道中的“水平”环境 -... p_n | p_n+1 | p_n+2 ...
管道中的进程是由同一个 shell 解释器生成的,因此无法更改环境变量,例如p_n
echiip_m
位于同一管道中。
如果您只需要对文件名进行简单的转换(可以使用正则表达式来实现),那么Perl 示例rename
应该做。您可能想要/需要使用xargs
来防止转义问题
find ... -print0 | xargs -0 rename "regexps"
它用 NUL 字节分隔文件名(NUL 和反斜杠是通常禁止出现在文件名中的唯一字符)。
如果您的文件名表现良好(因此您可以假设您不需要任何特殊的“奇怪”字符转义,例如正则表达式中使用的引号、空格和分隔符),您还可以做一些快速而肮脏的事情喜欢:
find ... | sed -r "regexps" | sh -
在这种情况下,正则表达式必须创建一个有效的命令调用mv
(或执行所需操作的其他命令) - 它可能如下所示:
"s|^.*$|mv -vi & &.old|"
这会产生类似的东西
...
mv -vi /etc/a2ps.cfg /etc/a2ps.cfg.old
mv -vi /etc/aclocal_dirlist /etc/aclocal_dirlist.old
...
如果简单的正则表达式还不够强大不然会太麻烦,只需编写一个简单的脚本来处理文件名并重命名/移动自身,然后直接从 find (-exec
或-exec +
)或通过 .find调用它xargs
。
最后,如果您只想将一堆文件的副本移动到另一个目录,请使用和-t
选项。GNU coreutils 中也有一个选项,它可以复制源及其完整路径。cp
mv
cp
--parents
答案3
为了以尽可能稳健的方式解决实际问题:
警告:命令行非常长。
find "${directory:-.}" -type f -name "*.dat" -exec sh -c 'for dat; do
if [[ -e $dat ]]; then
base_fn=${dat%.*}$((++n)).dat
base_fn=${dat##*/}
else
base_fn=${dat##*/}
fi
mv "$dat" "$base_fn"
done' _ {} +
这是便携式的;POSIX实际上将其指定-exec ... {} +
为最常见用法的替代品xargs
。我想, GNU 特有的东西mv -t
是很好的。唯一需要注意的问题是,如果没有 GNU's mv -t
,您需要mv
为每个文件生成一个新进程。
另一方面,扁平化目录结构可能会破坏具有相同基名的文件。我的方法将避免这种情况,除非在最极端的情况下(如果您的文件数量超出了 ARG_MAX 的容纳范围,和在第二次调用中,sh
您遇到了另一个重复的基本名称,该基本名称位于第一的调用,那么你就会失去第一个。)哦,好吧。这是一个很难编写代码的极端情况。