34

I have the following command:

find . -type d -mtime 0 -exec mv {} /path/to/target-dir \;

This will move the directory founded to another directory. How can I use xargs instead of exec to do the same thing.

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
zjhui
  • 779
  • 1
  • 8
  • 21

5 Answers5

43

With BSD xargs (for OS X and FreeBSD), you can use -J which was built for this:

find . -name some_pattern -print0 | xargs -0 -J % mv % target_location

That would move anything matching some_pattern in . to target_location

With GNU xargs (for Linux and Cygwin), use -I instead:

find . -name some_pattern -print0 | xargs -0 -I % mv % target_location

The deprecated -i option of GNU xargs implies -I{} and can be used as follows:

find . -name some_pattern -print0 | xargs -0 -i mv {} target_location

Note that BSD xargs also has a -I option, but that does something else.

Community
  • 1
  • 1
rakaur
  • 461
  • 4
  • 5
41

If you've got GNU mv (and find and xargs), you can use the -t option to mv (and -print0 for find and -0 for xargs):

find . -type d -mtime -0 -print0 | xargs -0 mv -t /path/to/target-dir

Note that modern versions of find (compatible with POSIX 2008) support + in place of ; and behave roughly the same as xargs without using xargs:

find . -type d -mtime -0 -exec mv -t /path/to/target-dir {} +

This makes find group convenient numbers of file (directory) names into a single invocation of the program. You don't have the level of control over the numbers of arguments passed to mv that xargs provides, but you seldom actually need that anyway. This still hinges on the -t option to GNU mv.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • @jlliagre: Have you looked up the meaning of the `-t` option? It is followed by the target directory name, and means that the last argument is not the target after all, thus enabling exactly this use with `xargs`. – Jonathan Leffler Dec 16 '12 at 09:41
  • 1
    Sorry, you are correct. I misread `-t` to be an `xarg` argument instead. – jlliagre Dec 16 '12 at 10:11
  • 2
    Great tip. One suggestion - instead of using that messy print0..-0 approach, use xargs -d'\n' – Sridhar Sarnobat Feb 02 '14 at 13:57
  • 3
    @Sridhar-Sarnobat: the `-print0` and `-0` approach works even for file names that contain newlines; your alternative does not. Granted, few files have newlines in their names, but the point of the null-terminated notation is that it covers _all_ file names without exception. – Jonathan Leffler Feb 02 '14 at 14:28
  • 1
    Thanks Jonathan. I didn't realize filenames could have newlines. – Sridhar Sarnobat Feb 03 '14 at 19:16
  • actually you can controll the number of arguments passed at once to the mv command by using ....xargs -0rn 100 mv....100 is the number of arguments – Jinxmcg Jul 10 '16 at 11:22
4
find ./ -maxdepth 1 -name "some-dir" -type d -print0 | xargs -0r mv -t x/

find: with option -print0, the output will end with '\0';

xargs: with option -0, it will split args by '\0' but whitespace, -r means no-run-if-empty, so you will not get any errors if find didn't get any output. (The -r is a GNU extension.)

I usually use this in scripts when I'm not sure if the target files exist or not.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
Evans Y.
  • 4,209
  • 6
  • 35
  • 43
2

find is not really a good tool for this. I imagine you want to move all subdirectories into another directory. find will output things like

./a
./a/b
./a/b/c
./a/b/c/d

After ./a gets moved first, you'll just get errors about 'no such file or directory' all the subdirs.

You should just use mv */ /another/place -- the trailing slash on the wildcard restricts the expansion to only dirs.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
0

If you are not using GNU mv, you can use that command:

find . -depth -type d -mtime 0 -exec bash -c 'declare -a array;j=1;for i; do array[$j]="$i"; j=$((j+1));done; mv "${array[*]}" /path/to/target-dir' arg0 {} +

otherwise, here is a simpler solution that doesn't require xargs:

find . -depth -type d -mtime 0 -exec mv -t /path/to/target-dir {} +

Note that I added -depth otherwise you'll have errors when both a directory and one of its subdirectory is to be processed.

jlliagre
  • 29,783
  • 6
  • 61
  • 72