xargs
is the tool for the job. That, or find
with -exec … {} +
. These tools run a command several times, with as many arguments as can be passed in one go.
Both methods are easier to carry out when the variable argument list is at the end, which isn't the case here: the final argument to mv
is the destination. With GNU utilities (i.e. on non-embedded Linux or Cygwin), the -t
option to mv
is useful, to pass the destination first.
If the file names have no whitespace nor any of \"'
, then you can simply provide the file names as input to xargs
(the echo
command is a bash builtin, so it isn't subject to the command line length limit; if you see !: event not found
, you need to enable globbing syntax with shopt -s extglob
):
echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir
You can use the -0
option to xargs
to use null-delimited input instead of the default quoted format.
printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir
Alternatively, you can generate the list of file names with find
. To avoid recursing into subdirectories, use -type d -prune
. Since no action is specified for the listed image files, only the other files are moved.
find . -name . -o -type d -prune -o \
-name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
-exec mv -t targetdir/ {} +
(This includes dot files, unlike the shell wildcard methods.)
If you don't have GNU utilities, you can use an intermediate shell to get the arguments in the right order. This method works on all POSIX systems.
find . -name . -o -type d -prune -o \
-name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
-exec sh -c 'mv "$@" "$0"' targetdir/ {} +
In zsh, you can load the mv
builtin:
setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/
or if you prefer to let mv
and other names keep referring to the external commands:
setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/
or with ksh-style globs:
setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/
Alternatively, using GNU mv
and zargs
:
autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/
Best Answer
Your error message argument list too long comes from the ***** of
ls *.txt
.This limit is a safety for both binary programs and your Kernel. See ARG_MAX, maximum length of arguments for a new process for more information about it, and how it's used and computed.
There is no such limit on pipe size. So you can simply issue this command:
NB: On modern Linux, weird characters in filenames (like newlines) will be escaped with tools like
ls
orfind
, but still displayed from *****. If you are on an old Unix, you'll need this commandNB2: I was wondering how one can create a file with a newline in its name. It's not that hard, once you know the trick: