I try to rename a bunch of filenames so that the periods (exept the last one) got replaced with a hypen using sed.
I tried it this way (see below), but it doesn't work... Whats wrong with the script?
echo "test.test.test.txt" | sed -e "s/\.(?=.*\.)/-/g"
results in:
test.test.test.txt
instead of the desired:
test-test-test.txt
I try to rename a bunch of filenames so that the periods (exept the last one) got replaced with a hypen using sed.
I tried it this way (see below), but it doesn't work... Whats wrong with the script?
echo "test.test.test.txt" | sed -e "s/\.(?=.*\.)/-/g"
results in:
test.test.test.txt
instead of the desired:
test-test-test.txt
7 Answers
Reset to default 3Assuming that you want to rename those files with mv
and that each filename will be in the variable $file
, then you can use a bash regex to split it into parts and a bash parameter expansion to convert the .
into -
:
#!/bin/bash
file=test.test.test.txt
[[ $file =~ ^(.+)(\..+)$ ]] &&
echo mv -- "$file" "${BASH_REMATCH[1]//./-}${BASH_REMATCH[2]}"
output:
mv -- test.test.test.txt test-test-test.txt
Using any sed you could change all .
s to -
s then the last -
back to a .
:
$ echo "test.test.test.txt" | sed 's/\./-/g; s/-\([^-]*\)$/.\1/'
test-test-test.txt
With GNU awk or other that supports the non-POSIX extension of a 3rd arg to match()
and gensub()
you could do:
$ echo "test.test.test.txt" | awk 'match($0,/(.*)(\..*)/,a){ print gensub(/\./,"-","g",a[1]) a[2] }'
test-test-test.txt
Sed doesn't support lookahead, as that's not part of a Basic Regular expression.
There are a few options available to get what you want:
Use Perl instead:
perl -pe 's/\.(?=.*\.)/-/g'
Reverse the string, and replace all
.
except the first one, then reverse it back again:rev | sed 's/\./-/2g' | rev
Replace one at a time in loop until there's no longer two dots in the pattern space:
#/usr/bin/sed -f :a /\..*\./ s/\./-/ ta
$ echo "test.test.test.txt" | sed -E 's/(.*)\.(.*)/\1\n\2/;s/\./-/g;s/\n/./'
test-test-test.txt
Replace last dot with newline, then all dots with -
, then newline with dot.
Using GNU awk
, change the field separator from .
to -
for all fields except last:
$ echo "test.test.test.txt" | awk -v FS='.' -v OFS='-' '{print $1,$2,$3 "." $4}'
test-test-test.txt
Using GNU sed
$ echo "test.test.test.txt" | sed -E ':a;s/([^.]*)\.(.*\.)/\1-\2/;ta'
test-test-test.txt
This might work for you (GNU sed):
sed -E 's/(.*)\./\1\n/;y/\n./.-/' file
Use greed to select the last .
and rename it (in this case to \n
a newline).
Then use translate to replace all other .
's with -
's and replace the renamed .
(I used a newline as it cannot be present in a pristine pattern space) back to .
.
rev
erse the string, and replace all but the first:echo ... | rev | sed 's/\./-/2g' | rev
– pmf Commented Feb 6 at 12:58(?=
- see Basic Regular Expressions. – Toby Speight Commented Feb 6 at 13:01