Gerco-Kees
Gerco-Kees

Reputation: 73

regex replace all dots exept last one doesn't work

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

Upvotes: 1

Views: 161

Answers (7)

potong
potong

Reputation: 58518

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 ..

Upvotes: 1

Fravadona
Fravadona

Reputation: 17216

Assuming 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

Upvotes: 3

sseLtaH
sseLtaH

Reputation: 11247

Using GNU sed

$ echo "test.test.test.txt" | sed -E ':a;s/([^.]*)\.(.*\.)/\1-\2/;ta'
test-test-test.txt

Upvotes: 1

Ed Morton
Ed Morton

Reputation: 204381

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

Upvotes: 2

Paolo
Paolo

Reputation: 26220

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

Upvotes: 1

Renaud Pacalet
Renaud Pacalet

Reputation: 29290

$ 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.

Upvotes: 1

Toby Speight
Toby Speight

Reputation: 30931

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:

  1. Use Perl instead:

    perl -pe 's/\.(?=.*\.)/-/g'
    
  2. Reverse the string, and replace all . except the first one, then reverse it back again:

    rev | sed 's/\./-/2g' | rev
    
  3. 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
    

Upvotes: 1

Related Questions