解决“mv:参数列表太长”?

解决“mv:参数列表太长”?

我有一个包含超过一百万个文件的文件夹需要排序,但我实际上无能为力,因为mv一直输出此消息

-bash: /bin/mv: Argument list too long

我使用此命令来移动无扩展名文件:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

答案1

xargs是完成这项工作的工具。那个,或者find-exec … {} +。这些工具多次运行一个命令,并一次性传递尽可能多的参数。

当变量参数列表位于末尾时,这两种方法都更容易执行,但这里的情况并非如此:最后一个参数mv是目的地。对于 GNU 实用程序(即在非嵌入式 Linux 或 Cygwin 上),-t选项mv很有用,可以首先传递目标。

如果文件名没有空格,也\"'没有任何空格,也不以-1 开头,那么您只需提供文件名作为输入xargs(该echo命令是 bash 内置命令,因此不受命令行长度限制;如果您看到!: event not found,则需要使用shopt -s extglob) 启用通配语法:

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir --

您可以使用该-0选项来xargs使用空分隔输入而不是默认的带引号的格式。

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir --

或者,您可以使用find.为了避免递归到子目录,请使用-type d -prune.由于没有为列出的图像文件指定任何操作,因此仅移动其他文件。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(这包括点文件,与 shell 通配符方法不同。)

如果您没有 GNU 实用程序,则可以使用中间 shell 以正确的顺序获取参数。此方法适用于所有 POSIX 系统。

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

在 zsh 中,您可以加载mv内置:

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

或者如果您希望让mv和其他名称继续引用外部命令:

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

或者使用 ksh 风格的 glob:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

或者,使用 GNUmvzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --

¹ 对于某些xargs实现,文件名也必须是当前语言环境中的有效文本。有些人还会考虑命名_为指示输入结束的文件(可以使用 来避免-E ''

答案2

如果使用 Linux 内核就足够了,你可以简单地这样做

ulimit -S -s unlimited

这是可行的,因为 Linux 内核在大约 10 年前包含了一个补丁,该补丁将参数限制更改为基于堆栈大小:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

如果你不想要无限的堆栈空间,你可以说例如

ulimit -S -s 100000

将堆栈限制为 100MB。请注意,您需要将堆栈空间设置为正常堆栈使用量(通常为 8 MB)加上您想要使用的命令行的大小。

您可以通过以下方式查询实际限额:

getconf ARG_MAX

这将输出最大命令行长度(以字节为单位)。例如,Ubuntu 默认设置为2097152大约 2 MB。如果我以无限堆栈运行,我得到的大小4611686018427387903正好是 2^62 或大约 46000 TB。如果您的命令行超过,我希望您能够自己解决该问题。

请注意,如果您在运行中使用sudoas 则无法解决该问题,因为在真正执行之前会重置堆栈大小。要解决这个问题,您必须使用 启动 root shell ,然后运行,最后在 root shell 中不运行该命令。sudo mv *.dat somewhere/.ulimitsudomvsudo -sulimit -S -s unlimitedsudo

答案3

有时最简单的方法是编写一个小脚本,例如用 Python:

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

答案4

操作系统的参数传递限制不适用于 shell 解释器内发生的扩展。因此,除了使用xargsor之外find,我们还可以简单地使用 shell 循环将处理分解为单独的mv命令:

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

这仅使用 POSIX Shell 命令语言功能和实用程序。这段文字通过缩进更加清晰,并删除了不必要的分号:

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

相关内容