Когда я использую эту команду 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 {} +
Ни один из этих вариантов не заботится о конфликтах имен. Вы должны тестировать данные, которые должным образом резервируются.