-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задача — разделить список аргументов, чтобы избежать этого ограничения. Здесь используется специфичная -tдля GNU опция 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 {} +

Ни один из этих вариантов не заботится о конфликтах имен. Вы должны тестировать данные, которые должным образом резервируются.

Связанный контент