Rename a file to parent directory's name in terminal
I have to deal with a large number of files nested inside of directories (one file per directory), resembling something like this:
fred/result.txt
john/result.txt
mary/result.txt
...
I am currently using the following command to process each of those files:
find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c \"cd '{}' && processResult result.txt\" \;
I am looking for something I can add into the end of this command that will change the filenames to fred.txt
, etc. and then later move the file into the parent directory to eliminate the extra layer of directories.
What would be the best way to do this?
We can use the classical approach : find
with while read
combo:
Demo:
$ ls *
fred:
results.txt
jane:
results.txt
john:
results.txt
$> find . -type f -name "results.txt" -printf "/%P\n" | while read FILE ; do DIR=$(dirname "$FILE" );\
> mv ."$FILE" ."$DIR""$DIR".txt;done
$> ls
fred/ jane/ john/
$> ls *
fred:
fred.txt
jane:
jane.txt
john:
john.txt
Use echo
before using mv
to test the line with your files.
Minor improvement
We can embed the call to bash
into exec , with parameter expansion (specifically prefix removal). Basically it's embedding a script into exec call.
$> find "$PWD" -type f -name "results.txt" -exec bash -c ' DIR=$( dirname "{}" ); echo "{}" "$DIR"/"${DIR##*/}".txt ' \;
/home/xieerqi/TESTDIR/fred/results.txt /home/xieerqi/TESTDIR/fred/fred.txt
/home/xieerqi/TESTDIR/jane/results.txt /home/xieerqi/TESTDIR/jane/jane.txt
/home/xieerqi/TESTDIR/john/results.txt /home/xieerqi/TESTDIR/john/john.txt
To make the command general, (working for any file, not just results.txt
) just remove -name "results.txt"
part. Also see this post for alternative Python solution to this.
The small python script below will do exactly as you describe; it will:
- rename any file in given directory, named
results.txt
, according to its superior directory - move it one (directory) level up
- delete the (now empty) directory where the file initially was located
The result:
This:
directory
|
├── fred
│ └── results.txt
├── john
│ └── results.txt
└── mary
└── results.txt
will become:
directory
|
├── fred.txt
│
├── john.txt
│
└── mary.txt
The script
#!/usr/bin/env python3
import shutil
import os
import sys
dr = sys.argv[1]
for root, dirs, files in os.walk(dr):
for file in files:
if file == "results.txt":
spl = root.split("/"); newname = spl[-1]; sup = ("/").join(spl[:-1])
shutil.move(root+"/"+file, sup+"/"+newname+".txt"); shutil.rmtree(root)
To use
- Copy the code below into an empty file, save it as
move_rename.py
-
Run it by the command:
python3 /path/to/move_rename.py <directory_to_reorganize>
Note
as always, first test it on a small sample
I think this is legit (using the perl-based rename
command)
find dir -name 'results.txt' -exec rename -nv -- 's|/(.*)/(.*)$|/$1/$1.txt|' {} +
e.g. given
$ tree dir
dir
├── fred
│ └── results.txt
├── john
│ └── results.txt
└── mary
└── results.txt
3 directories, 3 files
then
$ find dir -name 'results.txt' -exec rename -v -- 's|/(.*)/(.*)$|/$1/$1.txt|' {} +
dir/fred/results.txt renamed as dir/fred/fred.txt
dir/john/results.txt renamed as dir/john/john.txt
dir/mary/results.txt renamed as dir/mary/mary.txt
resulting in
$ tree dir
dir
├── fred
│ └── fred.txt
├── john
│ └── john.txt
└── mary
└── mary.txt
3 directories, 3 files
If you only need to go 1 level below dir
, you may be able to simply do
rename -vn -- 's|/(.*)/(.*)$|/$1/$1.txt|' dir/*/results.txt
As always, the -n
option is left in for testing purposes