Find and Act Quickly on Files
Today, I had a directory of files, and I wanted to copy a subset of those files into another directory, following the exact same directory structure. More specifically, I had a directory structure that looked like this:
> ls dir
2009/12/directory/one.mp3
2009/12/directory/one.mp3.1
2009/12/directory/two.mp3
2009/12/another_directory/three.mp3.1
2010/01/another_directory/fourty.mp3
...
In my case, I wanted to copy all the .1 files alone into another directory, such that I end up with:
> ls working_dir
2009/12/directory/one.mp3.1
2009/12/another_directory/three.mp3.1
Copying them by hand wasn’t an option, given that there were over 300 of these files spread out across these subdirectories.
I’ve really love the fd tool as a replacement for find
due to its speed and performance. Initially, I was trying to use a combination of fd
and shell scripting to do what I want. This worked initially, but I found edge cases where it didn’t work the way I expected it to. At that point, I remembered that fd
supports running commands on the file results. I didn’t realize just how much time this would save. I ended up accomplishing the above by using the following 2 commands:
fd -e 1 -x mkdir -p working_dir/{//}
fd -e 1 -x cp {} working_dir/{}
fd -e 1 -x rm
Let’s break this down:
fd -e 1
searches for files with the extension1
.-x
is for executing a command on each result (in parallel).mkdir -p
is what I want to do - if I haveoriginal/2009/12/directory/one.mp3.1
, I need to make the directory holding this file inworking_dir
to be able to copy the file to it.mkdir -p
allows me to do that, while making intermediate directories as required.{//}
is a placeholder infd
, which tells it to replace the result with the path of the result.
The second command uses {}
, a placeholder which will be replaced by the full path of the search result. The third command erases those files. Note the missing {}
, since if there is no placeholder, fd
will automatically add {}
at the end.
Consequently, these commands translate each search result into a mkdir
and then a cp
to accomplish what we want, followed by a deletion to remove the file - i.e.:
> 2020/06/directory/file.mp3.1
mkdir -p destination_directory/2020/06/directory
cp 2020/06/directory/file.mp3.1 destination_directory/2020/06/directory/file.mp3.1
rm 2020/06/directory/file.mp3.1
Awesome! There are also other placeholders, such as {.}
(file path without the extension), {/}
(file name without the path), and {/.}
(file without the path and without the extension).