當我使用這個命令時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 {} +
這些變體都不關心名稱衝突。您應該對正確備份的資料進行測試。