-bash: /usr/bin/mv: 参数列表太长

-bash: /usr/bin/mv: 参数列表太长

当我使用这个命令时mv * ..,我收到错误-bash: /usr/bin/mv: Argument list too long。我确实理解发生这种情况的原因是因为 bash 实际上将星号扩展到每个匹配的文件,产生一个非常长的命令行。我该如何修复这个错误?

答案1

限制不在 bash 中,而是在execve()用于执行外部命令的系统调用中。你可以这样做:

printf '%s\0' * | xargs -r0 mv -t .. --

由于printf是内置的,所以此时bash没有。execve()需要xargs分割参数列表以避免这种限制。这里使用 GNU-t的 GNU特定选项mv

使用zsh,您可以加载mv内置:

zmodload zsh/files
mv -- * ..

或者使用它的zargs助手来进行分割:

autoload -Uz zargs # best in ~/.zshrc
zargs -r -- ./* -- mv -t ..

您可以替换./*为来./*(D)移动隐藏文件,或者添加oNglob 限定符来跳过文件名排序,或者N添加 glob 限定符(带有zargs -r)来避免在没有匹配文件时出现错误。

zargs -r -- ./*(ND) -- mv -t ..

与...一样:

print -rNC1 ./*(ND) | xargs -r0 mv -t ..

但不依赖于 GNU xargs

在 Linux(通常在 Ubuntu 上使用的内核)上,您还可以execve()通过提高stacksize资源限制来提高该限制:

bash-5.1$ /bin/true {1..200000}
bash: /bin/true: Argument list too long
bash-5.1$ ulimit -s unlimited
bash-5.1$ /bin/true {1..200000}
bash-5.1$ 

它不是完全无限制的(至少在当前版本的内核中不是):

bash-5.1$ /bin/true {1..2000000}
bash: /bin/true: Argument list too long

请注意,限制在于传递给 的参数和环境变量的累积大小execve(),但计算不仅仅是其中字节的总和,而且其完成方式因操作系统及其版本而异。

答案2

您可以通过两个或更多步骤来完成:

mv [a-k]* ..    # or some other pattern matching a subset of the files
mv -- * ..

或者在一个循环中,

for name in *; do
    mv -- "$name" ..
done

(但这需要mv单独列出每个名字。)

或者来find帮助您:

find . -mindepth 1 -maxdepth 1 -exec mv -t .. -- {} +

这将找到当前目录中的所有名称,并使用 GNU mv,以尽可能少的调用将它们移动到上面的目录mv

没有 GNU mv,但find仍然知道非标准-mindepth-maxdepth谓词,

find . -mindepth 1 -maxdepth 1 -exec sh -c 'mv -- "$@" ..' sh {} +

这些变体都不关心名称冲突。您应该对正确备份的数据进行测试。

相关内容