Bash: Move an Entire Directory of Files
I recently wanted to nest all the files in a folder one level deeper. Which meant I needed to move all the files except for the nested folder I created. This will move all files, excluding the directory new-folder
:
ls|grep -v new-folder|xargs -I % mv % new-folder/
If you want to move only files and exclude all folders
ls -p|grep -v /|xargs -I % mv % new-folder/
And if you’re only moving folders and excluding files then run this:
ls -d */|grep -v new-folder/|xargs -I % mv % new-folder
Explaination
Let’s start with a directory and put some files in it:
$ mkdir my-folder
$ cd my-folder
$ touch f1.txt
$ touch f2.txt
$ touch f3.txt
$ touch f1.html
$ touch f2.html
$ touch f3.html
$ mkdir folder1
$ mkdir folder2
NOTE: For those new to the bash shell I am using $
to represent each command prompt. It is assumed you will not enter it as part of any commands in this post. The symbol will make it more clear which lines are commands and help delimit lists of commands.
Now let’s create the nested folder where we want to move our content:
$ mkdir new-folder
First, let’s break down the command starting with ls
:
$ ls
f1.html f1.txt f2.html f2.txt f3.html f3.txt folder1 folder2 new-folder
Next we would want to exclude our target directory new-folder
so we’ll pipe |
the output to grep
(I’ll get back to the -p
option later):
$ ls|grep -v new-folder
f1.html
f1.txt
f2.html
f2.txt
f3.html
f3.txt
folder1
folder2
The -v
option of grep
excludes a file so we can see that we get the entire list of files in the directory except our target.
xargs
By reading man xargs
we see the description as:
The xargs utility reads space, tab, newline and end-of-file delimited strings from the standard input and executes utility with the strings as arguments.
So it will read our newline delimited string, take each item and pass it as an argument to whichever command-line utility we specify.
The -I
option allows us to specify a string that we can use to specify where in our command string to put each item from standard input. Not all characters immediately work as a placeholder, so let’s experiment a little:
$ ls|grep -v new-folder|xargs -I % echo new-folder/%
Gives us:
new-folder/f1.html
new-folder/f1.txt
new-folder/f2.html
new-folder/f2.txt
new-folder/f3.html
new-folder/f3.txt
new-folder/folder1
new-folder/folder2
But if we change %
to #
:
$ ls|grep -v new-folder|xargs -I # echo new-folder/#
We get:
xargs: option requires an argument -- I
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
[-L number] [-n number [-x]] [-P maxprocs] [-s size]
[utility [argument ...]]
So it’s easier to find a couple single character symbols so you can choose whichever is the most clear for the given situation. However, if you really want to use a specific character or even multiple characters you can quote your replacement string/symbol like this:
$ ls|grep -v new-folder|xargs -I '#' echo new-folder/'#'
Or this:
$ ls|grep -v new-folder|xargs -I '{}' echo new-folder/'{}'
Will give you the same result as %
.
This also points out how to build your command string. Notice that the quotes only go around our replacement character. In fact whatever you specify immediately after -I
is exactly what you should use in your command string.
Directories vs. Files
The ls
command has a few options to let us distinguish between files and directories. For our purposes we will look at the -p
option to filter out directories and the -d
option to filter out files.
Here’s the output of ls -p
:
f1.html f1.txt f2.html f2.txt f3.html f3.txt folder1/ folder2/ new-folder/
Which appends /
to each directory. This means we can filter it with grep
like this:
$ ls -p|grep -v /
And our output will be:
f1.html
f1.txt
f2.html
f2.txt
f3.html
f3.txt
Which is now ready to be piped to xargs
. Now we’ll use the -d
option as follows:
$ ls -d */
Which will output:
folder1/ folder2/ new-folder/
We just need to filter out new-folder
using grep
:
$ ls -d */|grep -v new-folder
And now we get:
folder1/
folder2/
At this point you should have everything you need to execute commands against folders of objects. You can get much more sophisticated with the use of grep
of course but I’ll leave that as an exercise for the reader.