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
  • 1
    That was a bug: I showed how to load zf_mv and then used mv. Fixed. Commented Sep 17, 2014 at 21:12
  • reboot works for me Commented Aug 26, 2018 at 16:26

2 Answers 2

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.

4
  • That is not universal, that's FreeBSD/NetBSD/OS/X specific (for -E, -iregex, -J that are not standard). (ITYM xargs, not xargz btw) Commented Sep 17, 2014 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. Commented Sep 17, 2014 at 21:16
  • That will also break on file names that contain spacing or quoting characters. Commented Sep 17, 2014 at 21:16
  • that also recurses in the sub-directories which was not asked for. Commented Sep 17, 2014 at 21:22

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.