Reputation: 61
I have multiple files with this format: this-is-text_r_123.txt
and this-is-text.txt
.
What I would like to do (preferable using a for
loop) is to rename all this-is-text.txt
files to their corresponding this-is-text_r_123.txt
matches but have an i
instead of the r
in the file name. Considering that this-is-text
is a random text (different from one file to another) and the 123
in the example above is any combination of 3 numbers. All files are in one directory.
I tried with mv
and rename
but I wasn't successful
I've searched and reviewed all the file renaming questions here but none matched my case
Upvotes: 0
Views: 751
Reputation: 14811
I changed the technology to Python to demonstrate how to do this in a language more convenient than bash:
#!/usr/bin/python3
import glob
import re
text_files = glob.glob('*.txt')
#divide the files into two groups: "normal" files without _r and "target" files with _r
normal_files = {}
target_files = {}
for path in text_files:
#extract "key" (meaning part of file name without _r or _i)
#as well as whether the file contains _r or _i, or not
#using regular expressions:
result = re.match('(?P<key>.*?)(?P<target>_[ri]_?\d*)?\..*$', path)
if result:
if result.group('target'):
target_files[result.group('key')] = path
else:
normal_files[result.group('key')] = path
print(normal_files)
print(target_files)
#now figure out how to rename the files using the built dictionaries:
for key, path in normal_files.items():
if key in target_files:
target_path = target_files[key].replace('_r', '_i')
print('Renaming %s to %s' % (path, target_path))
For following set of files:
asd.txt
asd_r_1.txt
test test.txt
test test_r2.txt
another test_i_1.txt
this script will produce:
{'test test': 'test test.txt', 'asd': 'asd.txt'}
{'test test': 'test test_r2.txt', 'another test': 'another test_i_1.txt', 'asd': 'asd_r_1.txt'}
Renaming test test.txt to test test_i2.txt
Renaming asd.txt to asd_i_1.txt
You should be able to move files with this.
As you see, it works.
If you really need doing this in bash, it should be easy to port using sed
or awk
.
Upvotes: 0
Reputation: 14811
If you want to rename *.txt
to their _r_<NUMBER>.txt
counterparts and you're sure only one such file exists for each .txt
file, you can use following:
for x in *.txt
do
if [[ "$x" != *_r_* && "$x" != *_i_* ]]; then
y="${x%.*}"
echo "$x" "${y}_r_"*
fi
done
*.txt
files.
_r_*.txt
nor to-be-renamed-to _i_*.txt
file.$y
.*
glob star operator. If multiple files are matched, it will print all of them. If there are none, it will print only source file name. Depending on these circumstances, you might either move the file or keep it.To replace _r_
with _i_
in variable $z
, you might want to use z=${z/_r_/_i_}
. This will prove useful in point 1.2.2.
To move each *.txt
file and assign it a number:
i=0
for x in *.txt
do
let i+=1
y="$(echo "$x"|sed 's/\(\.[a-z]*\)$/_r_'"$i"'\1/')"
echo "$x" "$y"
done
i
and set it to 0.*.txt
files.
$i
it by 1 using let i+=1
.sed
, in which:
s/A/B/
) the file extension (.[a-z]*$
) with _r_
,$i
,\1
) by the brackets \(\)
in the left hand side of the s///
operator.'
and variables with "
. Note how quoting is changed twice in the expression.Seeing it in action:
rr-@herp:~$ i=0; for x in *.txt; do let i+=1; y="$(echo "$x"|sed 's/\(\.[a-z]*\)$/_r_'"$i"'\1/')"; echo "$x" "$y"; done
mm todo.txt mm todo_r_1.txt
mm.txt mm_r_2.txt
$i
-th file already exists, you can use if [ -f $target ]
.find
to find the files, but it's more complex and you should search on the web how to use find
with for
loops.Upvotes: 1