如何将 -I 和 -n 与 xargs 结合使用?

How do I combine -I and -n with xargs?

我想使用 findxargs 移动大量文件。通常我会这样做:

find /foo -name 'bar*' | tr '\n' ' ' | xargs -I % echo mv % /dest

但是,当要移动的文件太多时,我达到了传递给 mv 的参数数量的限制。 xargs 有一个 -n 似乎非常适合这个:

$ echo {0..9} | xargs -n 3 echo
0 1 2
3 4 5
6 7 8
9

但是,-I 意味着 -L 1,所以我不能将 -I-n 一起使用:

$ echo {0..9} | xargs -n 3 -I % echo % /dest
0 1 2 3 4 5 6 7 8 9 /dest

我希望有这样的行为:

$ echo {0..9} | xargs -n 3 -I % echo % /dest
0 1 2 /dest
3 4 5 /dest
6 7 8 /dest
9 /dest

xargs 可以吗?我的机器上没有 GNU Parallel。

mv 命令(至少来自 Linux coreutils)具有方便的 -t 标志,与此用例完美匹配:

find /foo -name 'bar*' | tr '\n' ' ' | xargs mv -t /dest

Above 还支持保留任何没有文件名的奇怪文件名 massaging:

find /foo -name 'bar*' -print0 | xargs -0 mv -t /dest

如果出于任何原因你想像往常一样使用 mv ,下面也可以工作(即使用 sh scriptlet 到 "consume" 所有参数( $@)):

find /foo -name 'bar*' | tr '\n' ' ' | xargs sh -c 'mv "$@" /dest' --

使用 GNU Parallel 你会做:

find /foo -name 'bar*' |
  parallel -X echo mv {} /dest

这适用于包含“、”和 space 的文件名。如果文件名包含“\n”,则您必须求助于:

find /foo -name 'bar*' --print0 |
  parallel -0 -X echo mv {} /dest

如果您的计算机上没有 GNU Parallel,并且您没有以 root 身份安装软件的权限,您可以进行个人安装:

wget https://ftpmirror.gnu.org/parallel/parallel-20180422.tar.bz2
bzip2 -dc parallel-20180422.tar.bz2 | tar xvf -
cd parallel-20180422
./configure --prefix=$HOME && make && make install

或者您可以在装有 GNU Parallel 的机器上使用 --embed,然后将生成的脚本复制到您的其他机器上:

parallel --embed > new_script
# Change the end of new_script
# Copy new_script to the other machine

如果您要使用 find 查找常规文件,您应该使用 -type f,否则您可能会找到目录。此处无需使用 xargs,因为您已经在 find.

中内置了该功能

使用 GNU mv:

find /foo -type f -name 'bar*' -exec mv -t /dest {} +

这不会检测到已经存在同名的目标文件。为此,请使用

find /foo -type f -name 'bar*' -exec sh -c '
    for name do
        destname="/dest/${name##*/}"
        if [ ! -f "$destname" ]; then
            mv "$name" "$destname"
        else
            printf '%s exists already, will not copy %s\n' "$destname" "$name" >&2
        fi
    done' sh {} +

这将拒绝移动会覆盖 /dest 中文件的文件(并报告这些文件)。

这些命令适用于所有有效的 Unix 文件名。