I know you can do it with a find
, but is there a way to send the output of ls
to mv
in the unix command line?

- 1,414
- 1
- 16
- 32
-
You can send the output of almost any command to almost any other command with a pipe ('|') character ... but I don't think this is what you really want to do. Can you explain what you'd like to accomplish? – Adam Liss Jun 02 '09 at 02:47
-
It would help if you describe your problem with the find command. – nik Jun 02 '09 at 02:51
-
1find + xargs is likely the best way of doing what you want. Why do you insist on using ls + mv? – JesperE Jun 02 '09 at 05:13
-
3@JesperE: find + xargs is silly. find has a -exec option which is way more reliable and there's no need for convoluted piping, not even any need for shell logic. – lhunath Jun 02 '09 at 05:53
13 Answers
ls
is a tool used to DISPLAY some statistics about filenames in a directory.
It is not a tool you should use to enumerate them and pass them to another tool for using it there. Parsing ls
is almost always the wrong thing to do, and it is bugged in many ways.
For a detailed document on the badness of parsing ls, which you should really go read, check out: http://mywiki.wooledge.org/ParsingLs
Instead, you should use either globs or find, depending on what exactly you're trying to achieve:
mv * /foo
find . -exec mv {} /foo \;
The main source of badness of parsing ls
is that ls dumps all filenames into a single string of output, and there is no way to tell the filenames apart from there. For all you know, the entire ls
output could be one single filename!
The secondary source of badness of parsing ls
comes from the broken way in which half the world uses bash. They think for
magically does what they would like it to do when they do something like:
for file in `ls` # Never do this!
for file in $(ls) # Exactly the same thing.
for
is a bash builtin that iterates over arguments. And $(ls)
takes the output of ls
and cuts it apart into arguments wherever there are spaces
, newlines
or tabs
. Which basically means, you're iterating over words, not over filenames. Even worse, you're asking bask to take each of those mutilated filename words and then treat them as globs that may match filenames in the current directory. So if you have a filename which contains a word which happens to be a glob that matches other filenames in the current directory, that word will disappear and all those matching filenames will appear in its stead!
mv `ls` /foo # Exact same badness as the ''for'' thing.

- 120,288
- 16
- 68
- 77
-
`find . -exec mv {} /foo \;`, what does the `{}` mean here? Is there a time when we put something inside `{}`? – Thang Pham Jul 28 '11 at 18:17
-
On my machine, `man find | grep {}` says that: A command parameter {} (braces) is replaced by the current path name. – stevepastelan Dec 11 '13 at 23:49
-
@ThangPham {} is where find puts the current filename. You never put anything inside the {} and it should always be standing as an argument by itself (ie. never "inside" an argument like "{}.txt". – lhunath Dec 13 '13 at 11:44
-
What if I want to move identically named files in another folder? e.g. dir1 has 100 files. dir2 has 53 files, identically named after 53/100 files in dir1. I want to move 53/100 files in dir1. I don't think this is working then. – Saren Tasciyan Mar 26 '22 at 10:00
One way is with backticks:
mv `ls *.boo` subdir
Edit: however, this is fragile and not recommended -- see @lhunath's asnwer for detailed explanations and recommendations.

- 854,459
- 170
- 1,222
- 1,395
-
13Which is *exactly* the same as the simpler "mv *.boo subdir" *except* yours won't handle files with spaces in their names and it won't work if you have "alias ls='ls --color=always'". You should never rely on the output of ls in this way. – Dennis Williamson Jun 02 '09 at 04:11
-
could always call ls directly, ie whereis ls, then '/bin/ls' or whatever is returned. – PostMan Jun 02 '09 at 04:32
-
2That still doesn't fix the problem with spaces in filenames. – Dennis Williamson Jun 02 '09 at 04:52
-
@Dennis, good point -- better not to have filenames with spaces, but unless you can guarantee that is the case then backticks, xargs (without the -0 trick), etc, can all break. – Alex Martelli Jun 02 '09 at 04:54
-
-
I cant do this: any idea why? `mv \`ls -l | grep 2011-08-24\` subdir` – Thang Pham Aug 24 '11 at 21:09
-
@ThangPham: because you shouldn't use `ls` in this way in the first place, but the precise reason it's not working is because you included the `-l` option which is trying to move a file named something like `-rw-r--r-- 1 thang thang 5263 May 23 2017 something_2011-08-24_something` instead of its actual name `something_2011-08-24_something`. The _correct_ command to do what you want would be `mv *2011-08-24* subdir` – Dennis Williamson May 18 '18 at 15:33
-
This worked out for me because I did not have spaces and I wanted to move identically named files in another folder e.g. dir1 has 100 files. dir2 has 53 files, identically named after 53/100 files in dir1. I want to move 53/100 files in dir1. @lhunath's solution did not help there. – Saren Tasciyan Mar 26 '22 at 10:02
None of the answers so far are safe for filenames with spaces in them. Try this:
for i in *; do mv "$i" some_dir/; done
You can of course use any glob pattern you like in place of *
.

- 25,752
- 9
- 89
- 101
Not exactly sure what you're trying to achieve here, but here's one possibility:
The "xargs" part is the important piece everything else is just setup. The effect of this is to take everything that "ls" outputs and add a ".txt" extension to it.
$ mkdir xxx # $ cd xxx $ touch a b c x y z $ ls a b c x y z $ ls | xargs -Ifile mv file file.txt $ ls a.txt b.txt c.txt x.txt y.txt z.txt $
Something like this could also be achieved by:
$ touch a b c x y z $ for i in `ls`;do mv $i ${i}.txt; done $ ls a.txt b.txt c.txt x.txt y.txt z.txt $
I sort of like the second way better. I can NEVER remember how xargs works without reading the man page or going to my "cute tricks" file.
Hope this helps.

- 1,575
- 1
- 11
- 19
-
@kungfugraig: note that @David Williamson's remarks on filenames containing spaces apply to your xargs use, too. – Alex Martelli Jun 02 '09 at 04:58
-
3There's no particular need for the `ls` - "for i in *; do whatever; done" works too. – C Pirate Jun 02 '09 at 05:20
Check out find -exec {}
as it might be a better option than ls
but it depends on what you're trying to achieve.

- 3,128
- 18
- 19
-
5You should give an example, like “find . -name '*.txt' -exec cp '{}' /stuff ';'”. Especially since it's not obvious to get the quoting correct. – Josh Lee Jun 02 '09 at 05:08
You shouldn't use the output of ls as the input of another command. Files with spaces in their names are difficult as is the inclusion of ANSI escape sequences if you have:
alias ls-'ls --color=always'
for example.
Always use find or xargs (with -0) or globbing.
Also, you didn't say whether you want to move files or rename them. Each would be handled differently.
edit: added -0 to xargs (thanks for the reminder)

- 346,391
- 90
- 374
- 439
-
xargs has problems with spaces in names, too (unless it's fed from, say, by a find's -print0, and run with a -0 itself). – Alex Martelli Jun 02 '09 at 04:59
-
...which it always should be in there is any risk of paths having whitespace in them – JesperE Jun 02 '09 at 05:11
-
moreover xargs also tries to eat quote characters contained in your filenames if not ran with the -0 option. I recommend you remove xargs from the response or add that -0 is an absolute must. – lhunath Jun 02 '09 at 05:51
/bin/ls | tr '\n' '\0' | xargs -0 -i% mv % /path/to/destdir/
"Useless use of ls", but should work. By specifying the full path to ls(1) you avoid clashes with aliasing of ls(1) mentioned in some of the previous posts. The tr(1) command together with "xargs -0" makes the command work with filenames containing (ugh) whitespace. It won't work with filenames containing newlines, but having filenames like that in the file system is to ask for trouble, so it probably won't be a big problem. But filenames with newlines could exist, so a better solution would be to use "find -print0":
find /path/to/srcdir -type f -print0 | xargs -0 -i% mv % dest/

- 9,246
- 2
- 25
- 22
-
-
This should be the accepted answer as it best addresses the question (and works for all reasonable basic use cases). I was able to easily adapt this answer to piping the output of `fdupes` into it (instead of `ls`), so it was super-helpful to me. Thank you! – Timotheos Mar 19 '22 at 02:38
\ls *.png -tmQ1 | sed "s/^/viewnior /" > dw && chmod u+x dw && ./dw
The above uses ls because I needed things sorted by time. It inserts the command viewnior (to view .png files) (in your case the command would be mv) on each line and redirects the output to a file. That file is then made executable and each line in this file is a command like this: viewnior image.png. Next it executes the file. It may not be pretty but I find it easy to remember.
(The \ in \ls is there in case ls was aliased to something that may interfere.)

- 1
-
What do you mean by "it executes the file"? Do you mean that the output is an image of the file list? – ryanwebjackson May 24 '23 at 17:04
-
Backticks work well, as others have suggested. See xargs, too. And for really complicated stuff, pipe it into sed, make the list of commands you want, then run it again with the output of sed piped into sh.
Here's an example with find, but it works fine with ls, too:
http://github.com/DonBranson/scripts/blob/f09d24629ab6eb3ce509d4d3078818430306b063/jarfinder.sh

- 13,631
- 10
- 59
- 101
-
Downvotes? Seriously? :( I acknowledged the other good answers, and gave a couple additional approaches that work in situations where the backticks won't. – Don Branson Jun 02 '09 at 12:12
So this answer doesn't send the output of ls
to mv
but as @lhunath explained using ls
is almost always the wrong tool for the job. Use shell globs or a find command.
For more complicated cases (often in a script), using bash arrays to build up the argument list from shell globs or find
commands can be very useful. One can create an array and push to it with the appropriate conditional logic. This also handles spaces in filenames properly.
For example:
myargs=()
# don't push if the glob does not match anything
shopt -s nullglob
myargs+=(myfiles*)
To push files matching a find to the array: https://stackoverflow.com/a/23357277/430128.
The last argument should be the target location:
myargs+=("Some target directory")
Use myargs
in the invocation of a command like mv
:
mv "${myargs[@]}"
Note the quoting of the array myargs
to pass array elements with spaces correctly.

- 17,606
- 5
- 95
- 112
#!/bin/bash
for i in $( ls * );
do
mv $1 /backup/$1
done
else, it's the find solution by sybreon, and as suggested NOT the green mv ls solution.

- 1,071
- 1
- 10
- 15
Just use find
or your shells globing!
find . -depth=1 -exec mv {} /tmp/blah/ \;
..or..
mv * /tmp/blah/
You don't have to worry about colour in the ls output, or other piping strangeness - Linux allows basically any characters in the filename except a null byte.. For example:
$ touch "blah\new|
> "
$ ls | xargs file
blahnew|: cannot open `blahnew|' (No such file or directory)
..but find works perfectly:
$ find . -exec file {} \;
./blah\new|
: empty

- 165,801
- 69
- 278
- 343
You surround the ls with back quotes and put it after the mv, so like this...
mv `ls` somewhere/
But keep in mind that if any of your file names have spaces in them it won't work very well.
Also it would be simpler to just do something like this: mv filepattern* somewhere/

- 47,594
- 12
- 108
- 150

- 405
- 2
- 6