Pipe to mv command
I'm trying to move files from Terminal but only those from the results of a grep
query.
ls -l | grep -i s02 | mv
What is the best way to complete the command above?
You can accomplish this by just running
cd directory/containing/the/files
mv *[sS]02* /path/to/target/
For more complex operations there is also the option to use find
to find all relevant files. The example from your question could also be written as
cd directory/containing/the/files
find . -type f -maxdepth 1 -iname '*s02*' -exec mv {} /path/to/target/directory/ \;
It's worth to have a look at man find
to see which other options are available.
For example with bash. Given the files below
$ ls -1
test1
test2
test3
1) To avoid surprises let's take a look at the commands first
$ for i in test*; do echo mv $i $i.ext; done
mv test1 test1.ext
mv test2 test2.ext
mv test3 test3.ext
and execute the commands if this is what we want
$ for i in test*; do mv $i $i.ext; done
$ ls -1
test1.ext
test2.ext
test3.ext
Fit the path to your needs.
2) It is possible to use sed and xargs. Let's test it first
$ ls -1 test* | sed 'p;s/test/test_/' | xargs -n2 printf "%s %s\n"
test2 test_2
test1 test_1
test3 test_3
and execute the commands if this is what we want
$ ls -1 test* | sed 'p;s/test/test_/' | xargs -n2 mv
$ ls -1
test_1
test_2
test_3
Spaces in the filename
These scripts won't work as expected if the filenames include spaces. In Unix space is used as a separator. For example with the files
$ ls -1
test 1
test 2
test 3
it is clear that the commands below can't work as expected
$ for i in test*; do echo mv $i $i.ext; done
mv test 1 test 1.ext
mv test 2 test 2.ext
mv test 3 test 3.ext
The solution is to exclude the space from the list of separators
$ IFS="`printf '\n\t'`"
and prepend it to the commands that should process the filenames with spaces. The command below
$ IFS="`printf '\n\t'`"; for i in test*; do mv $i $i.ext; done
works as expected
$ ls -1
test 1.ext
test 2.ext
test 3.ext
Next option is the quotation of the arguments
for i in test*; do mv "$i" "$i.ext"; done
This way it's possible to use the rich variety of Unix filters and avoid the limitation to the "find ... -exec ... {}" construct. For other limitations see the notes under the line.
Notes on macOS
1) Quoting from opengroup.org - Product Standard: Commands and Utilities V4
1.4.21 sh
Question 31: Is the environment variable IFS ignored when the shell is invoked?
Response: Yes
Rationale: The specification allows the sh command ignore the setting of the IFS environment variable on invocation. The setting of this variable has been used to breach security on systems which use the shell to interpret a call to the system() and execvp() interfaces.
Reference: Technical Standard, Shell and Utilities, Issue 6, Chapter 4, Utilities, sh, ENVIRONMENT VARIABLES, IFS.
2) Quoting from developer.apple.com - Shell Input and Output
You can modify the behavior of the read command by modifying the shell variable IFS (short for internal field separators). The default behavior is to split inputs everywhere there is a space, tab, or newline. By changing this variable, ...
3) Quoting from developer.apple.com - Basic Control Statements - Standard for Loops
In the next example, the list is *.JPG ...
#!/bin/sh
for i in *.JPG ; do
mv "$i" "$(echo $i | sed 's/\.JPG$/.x/')"
mv "$(echo $i | sed 's/\.JPG$/.x/')" "$(echo $i | sed 's/\.JPG$/.jpg/')"
done
These scripts may not work as expected if there are special characters in the filename. Replace such characters first. See How to remove special character. There are dozens of articles how to rename files with spaces and other characters that make troubles e.g. Filenames with spaces breaking for loop, find command. It's a good idea to get rid of such characters and translate the filenames to the format you prefer (e.g. rename-file) before processing them in *nix. See Fixing Unix/Linux/POSIX Filenames.