当我使用这个命令时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)
移动隐藏文件,或者添加oN
glob 限定符来跳过文件名排序,或者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 {} +
这些变体都不关心名称冲突。您应该对正确备份的数据进行测试。