6

I read in this answer from @Gilles the following:

In zsh, you can load the mv builtin:

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
mv -- ^*.(jpg|png|bmp) targetdir/

as a solution to the "mv: Argument list too long” problem. The answer suggests using zsh's mv (as opposed to GNU's) but what exactly does this line do?:

zmodload -Fm zsh/files b:zf_\*

2 Answers2

13

The best way, to look at zsh documentation is using info.

If you run info zsh, you can use the index (think of a book's index) to locate the section that describes the zmodload command.

Press i, then you can enter zmo and press Tab. You'll get straight to the zmodload builtin description which will tell you all about it.

In short, zmodload -F loads the module (if not loaded) and enables only the specified features from that module.

With -m, we enabled the features that match a pattern, here b:zf_*. b: is for builtin, so the above command loads the zsh/files module (see info -f zsh -n 'The zsh/files Module,' for details on that) and only enables the builtins whose name starts with zf_.

zmodload -F zsh/files

loads the module, but doesn't enable any feature:

$ zmodload -FlL zsh/files
zmodload -F zsh/files -b:chgrp -b:chown -b:ln -b:mkdir -b:mv -b:rm -b:rmdir -b:sync -b:zf_chgrp -b:zf_chown -b:zf_ln -b:zf_mkdir -b:zf_mv -b:zf_rm -b:zf_rmdir -b:zf_sync

lists the features of that module specifying which are currently enabled (none for now). You'll notice there's both a mv and zf_mv builtin.

$ zmodload -mF zsh/files 'b:zf_*'
$ zmodload -FlL zsh/files
zmodload -F zsh/files -b:chgrp -b:chown -b:ln -b:mkdir -b:mv -b:rm -b:rmdir -b:sync +b:zf_chgrp +b:zf_chown +b:zf_ln +b:zf_mkdir +b:zf_mv +b:zf_rm +b:zf_rmdir +b:zf_sync

You'll notice the zf_mv builtin has been enabled, but not the mv one (same for the other builtins). That means, those builtin versions of the system commands have been enabled, but without overriding the system one:

$ type zf_mv
zf_mv is a shell builtin
$ type mv
mv is /bin/mv

Now that you have a builtin mv, as zf_mv, not mv, you can do:

zf_mv -- ^*.(jpg|png|bmp) targetdir/

Because zf_mv is builtin, there's no execve() system call, so you won't hit the Too many args limit associated with it.

Of course, you can also do:

zmodload zsh/files # without -F, all the features are enabled
mv -- ^*.(jpg|png|bmp) targetdir/

But beware that replaces the system's mv with zsh builtin equivalent.

To overcome the E2BIG execve() error (the Too many args upon executing an external command), zsh also provides with a zargs function.

You run:

autoload zargs # in ~/.zshrc if you use it often

To mark it for autoloading.

Then you can use:

zargs -- ^*.(jpg|png|bmp) -- mv -t targetdir/

(here assuming GNU mv for the -t option). zargs will run as many mv commands as necessary to avoid the E2BIG (as xargs would do).

-3

There is an universal solution that not rely on bashism: xargs.

xargs reads a stream of arguments from stdin and then pass them to the arbitrary specified utility. In your case that is how command line should look like:

# find -E /source/dir -iregex ".*\.(jpg|png|bmp)" | xargz -n 1 -J % mv % /target/dir

Here -E means that find used the modern regexes (for|alternatives), -n 1 means that incoming stream will be splitted into single arguments, -J % means that each argument will be substituted at the next occurence of the % char.

-J % is necessary because xargs appends an argument to the end of child command by default. Absence of -J % is equivalent to the xargs -n 1 -J % mv /target/dir % that is definitely not what you want.

Kondybas
  • 695
  • 5
  • 9
  • That is not universal, that's FreeBSD/NetBSD/OS/X specific (for -E, -iregex, -J that are not standard). (ITYM xargs, not xargz btw) – Stéphane Chazelas Sep 17 '14 at 21:15
  • There is no bashism here. It's a zsh feature. And the question isn't about doing this (that's what the linked question was about, and I give several solutions, including portable ones, unlike yours which only works on BSD because of -J and chokes on several characters in file names), but how this particular zsh solution works. – Gilles 'SO- stop being evil' Sep 17 '14 at 21:16
  • That will also break on file names that contain spacing or quoting characters. – Stéphane Chazelas Sep 17 '14 at 21:16
  • that also recurses in the sub-directories which was not asked for. – Stéphane Chazelas Sep 17 '14 at 21:22