62

How do I pipe the results of a 'find' (in Linux) to be moved to a different directory? This is what I have so far.

find ./ -name '*article*' | mv ../backup

but its not yet right (I get an error missing file argument, because I didn't specify a file, because I was trying to get it from the pipe)

user1015214
  • 2,733
  • 10
  • 36
  • 66

6 Answers6

92
find ./ -name '*article*' -exec mv {}  ../backup  \;

OR

find ./ -name '*article*' | xargs -I '{}' mv {} ../backup
Amit
  • 19,780
  • 6
  • 46
  • 54
  • when I ran your second command, it says xargs: invalid option -- I. What is wrong here? I see that it is a valid option on the man page. – user1015214 Mar 13 '14 at 19:30
  • @user1015214 - Is none of the 4 commands on this page working for you? – Amit Mar 13 '14 at 19:47
  • no. If it helps, I am in a git Bash on a windows machine. It seems to be running linux commands, but I guess it doesn't have the full library? – user1015214 Mar 13 '14 at 20:11
  • @user1015214 - Probably git bash is limited. Since you are on windows machine, I'd suggest using `Move-Item Cmdlet` in `Powershell`. You can do `Move-Item *article* \full\path\to\backup` – Amit Mar 13 '14 at 21:14
43

xargs is commonly used for this, and mv on Linux has a -t option to facilitate that.

find ./ -name '*article*' | xargs mv -t ../backup

If your find supports -exec ... + instead of -exec ... \; you could equivalently do

find ./ -name '*article*' -exec mv -t ../backup {} +

The -t option is a GNU extension, so it is not portable to systems which do not have GNU coreutils (though every proper Linux I have seen has that, with the possible exception of Busybox). For complete POSIX portability, it's of course possible to roll your own replacement, maybe something like

find ./ -name '*article*' -exec sh -c 'mv "$@" "$0"' ../backup {} +

where we shamelessly abuse the convenient fact that the first argument after sh -c 'commands' ends up as the "script name" parameter in $0 so that we don't even need to shift it.

Probably see also https://mywiki.wooledge.org/BashFAQ/020

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • When I tried you first suggestion, it said 'invalid option -- t'. When I tried your second one, it said -exec command not found, so I guess its not supported. – user1015214 Mar 13 '14 at 19:22
  • Sounds like you are not on Linux after all then, as this has been supported by the GNU versions of these tools for a long time. I can't find (sic) a `find` which doesn't support `-exec` in *some* form -- maybe you mistyped the command? – tripleee Jan 20 '17 at 10:18
  • Actually Busybox supports `mv -t` too. – tripleee May 24 '22 at 04:04
  • Note if you use this, your directories cannot have spaces in them. – kannavkm Mar 09 '23 at 03:21
  • @exclowd The `find -exec` variant is specifically robust against all kinds of problems with unusual file names. The `xargs` variant is slightly less so, though the linked FAQ has a more elaborate variation which requires GNU `find` (so basically Linux only). In brief, `find ... -print0 | xargs -r0 mv -t ../backup` – tripleee Mar 09 '23 at 05:59
3

I found this really useful having thousands of files in one folder:

ls -U | head -10000 | egrep '\.png$' | xargs -I '{}' mv {} ./png

To move all pngs in first 10000 files to subfolder png

Steffan
  • 704
  • 1
  • 11
  • 25
  • 1
    The regex has no `egrep` (aka `grep -E` more modernly) syntax, and you can factor out the `head`: `grep -m 10000 '\.png$'`, More generally you should [avoid `ls` in scripts](https://mywiki.wooledge.org/ParsingLs), – tripleee Feb 27 '18 at 04:21
  • Perhaps then `printf '%s\0' *.png | xargs -r0 mv -t png/` which avoids the various issues with `ls`, and `printf '%s\0' *.png | grep -z -m 10000 ^ | xargs -r0 mv -t png/` to coincidentally use `grep -z -m` *items* as a replacement for `head` which groks null-terminated input. The link I posted earlier explains these things in much more detail. I'm divided about improving an answer which advocates `ls`; but feel free to update as you see fit, and include anything or everything from these comments. Incidentally, `xargs -0` is GNU only (should work on Linux, but isn't properly portable). – tripleee Feb 28 '18 at 17:49
0
mv $(find . -name '*article*') ../backup
Vencat
  • 1,272
  • 11
  • 36
  • 1
    this command is not correct. it says `missing file operand`. (I used -t before ../backup to define the destination directory) – Farbod Shahinfar Jan 17 '19 at 11:21
  • It's working for me. Your backup directory should be placed back to your search directory. – Vencat Jan 18 '19 at 07:30
  • 2
    This is susceptible to generating invalid file names. The command substitution to generate the list of source file names is inherently problematic. It will look like it's working if you test it on the trivial cases, but it will break horribly if you have filenames with spaces or shell metacharacters in them. Several items on https://mywiki.wooledge.org/BashPitfalls are variations of this FAQ. – tripleee Aug 12 '19 at 05:00
0

Here are a few solutions.

find . -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
    -exec mv {} path \;**

or

find path -type f -newermt "2019-01-01" ! -newermt "2019-05-01" \
    -exec mv {} path \;

or

find /Directory/filebox/ -type f -newermt "2019-01-01" \
    ! -newermt "2019-05-01" -exec mv {} ../filemove/ \;

The backslash + newline is just for legibility; you can equivalently use a single long line.

tripleee
  • 175,061
  • 34
  • 275
  • 318
hizlan erpak
  • 309
  • 2
  • 3
-1

xargs is your buddy here (When you have multiple actions to take)!

And using it the way I have shown will give great control to you as well.

find ./ -name '*article*' | xargs -n1 sh -c "mv {}  <path/to/target/directory>"

Explanation:

  1. -n1

Number of lines to consider for each operation ahead

  1. sh -c

The shell command to execute giving it the lines as per previous condition

  1. "mv {} /target/path"

The move command will take two arguments-

1) The line(s) from operation 1, i.e. {}, value substitutes automatically

2) The target path for move command, as specified

Note: the "Double Quotes" are specified to allow any number of spaces or arguments for the shell command which receives arguments from xargs

piet.t
  • 11,718
  • 21
  • 43
  • 52
nitinr708
  • 1,393
  • 2
  • 19
  • 29
  • 1
    Why would you require `-n1` and what is then the purpose of the `sh -c` wrapper? This onl. brings drawbacks compared to the other, older, similar answers. – tripleee Feb 27 '18 at 04:24
  • Thank you for the feedback. I have been using this syntax for quite some time. Can you enlighten me on the drawbacks? It will only help me be better informed – nitinr708 Feb 27 '18 at 08:38
  • 1
    The `sh -c` is useless if you only ever loop over a single argument. On the other hand, the `-n 1` is basically making `xargs` useless too; just run `find -exec` if you only move one file at a time. Processing only one file at a time is hugely wasteful, though; you should generally attempt to minimize the number of subprocesses. The previous answers implement various optimizations to do exactly that, some more aggressively and robustly than others. – tripleee Feb 27 '18 at 08:39