I have to do a sed line (also using pipes in Linux) to change a file extension, so I can do some kind of mv *.1stextension *.2ndextension
like mv *.txt *.c
. The thing is that I can't use batch or a for loop, so I have to do it all with pipes and sed command.

- 46,058
- 19
- 106
- 116

- 781
- 2
- 8
- 16
-
2Why you can't use loop ? You should make it clear ! Are you doing this as a part of a homework ? – iamauser Oct 03 '13 at 17:38
-
of course i am, i didn't mentioned but i think it's pretty known because of teh restrictions – heythatsmekri Oct 05 '13 at 21:38
7 Answers
you can use string manipulation
filename="file.ext1"
mv "${filename}" "${filename/%ext1/ext2}"
Or if your system support, you can use rename
.
Update
you can also do something like this
mv ${filename}{ext1,ext2}
which is called brace expansion

- 10,869
- 1
- 27
- 46
-
I wonder if we are allowed to user `rename` ...it is available in most Linux system – CS Pei Oct 03 '13 at 17:19
-
Thanks, but i cant use any batch or script sentence, I just can use my command line, sed and pipes – heythatsmekri Oct 03 '13 at 17:22
-
what exactly your input? what kind of output do you expect? if only `sed` allowed I don't think you can rename a file. – CS Pei Oct 03 '13 at 17:25
-
2@user2113403: `for filename in *.1stextension; do mv "${filename}" "${filename/%1stextension/2ndextension}"; done` will do for a bunch of files. No need for `sed`. – bitmask Oct 03 '13 at 19:44
-
sed is for manipulating the contents of files, not the filename itself. My suggestion:
rename 's/\.ext/\.newext/' ./*.ext
Or, there's this existing question which should help.
-
I had to substitute .c with .o in a Makefile. This solved my problem, thanks. – Garini Jul 06 '17 at 17:04
This may work:
find . -name "*.txt" |
sed -e 's|./||g' |
awk '{print "mv",$1, $1"c"}' |
sed -e "s|\.txtc|\.c|g" > table;
chmod u+x table;
./table
I don't know why you can't use a loop. It makes life much easier :
newex="c"; # Give your new extension
for file in *.*; # You can replace with *.txt instead of *.*
do
ex="${file##*.}"; # This retrieves the file extension
ne=$(echo "$file" | sed -e "s|$ex|$newex|g"); # Replaces current with the new one
echo "$ex";echo "$ne";
mv "$file" "$ne";
done
-
I'm supposed not to use any loop or sentence, just pipes, comand line and sed – heythatsmekri Oct 03 '13 at 17:21
You can use find
to find all of the files and then pipe that into a while read
loop:
$ find . -name "*.ext1" -print0 | while read -d $'\0' file
do
mv $file "${file%.*}.ext2"
done
The ${file%.*}
is the small right pattern filter. The %
marks the pattern to remove from the right side (matching the smallest glob pattern possible), The .*
is the pattern (the last .
followed by the characters after the .
).
The -print0
will separate file names with the NUL
character instead of \n
. The -d $'\0'
will read in file names separated by the NUL
character. This way, file names with spaces, tabs, \n
, or other wacky characters will be processed correctly.

- 105,218
- 39
- 216
- 337
-
-
It's a pipe. As the OP said "_so i have to do it all with **pipes** and sed command_". I'm not sure what the issue the OP was having with `for`, but `for` can be troublesome because of the way it parses the parameters for the loop. Unfortunately, the OP didn't provide much information what they are doing. With more info, I could give a better answer. – David W. Oct 03 '13 at 17:33
-
Agree with you that OP doesn't provide enough information, why not loop ! – iamauser Oct 03 '13 at 17:45
You may try following options
Option 1 find
along with rename
find . -type f -name "*.ext1" -exec rename -f 's/\.ext1$/ext2/' {} \;
Option 2 find
along with mv
find . -type f -name "*.ext1" -exec sh -c 'mv -f $0 ${0%.ext1}.ext2' {} \;
Note: It is observed that rename
doesn't work for many terminals

- 11,387
- 6
- 35
- 45
-
@iamauser try using option 2 `mv` if `rename` is not working. Even I am not sure why `rename` doesn't work for many terminals. For me it works in `ubuntu` but not in `RHEL` – jkshah Oct 03 '13 at 17:51
Another solution only with sed and sh
printf "%s\n" *.ext1 |
sed "s/'/'\\\\''/g"';s/\(.*\)'ext1'/mv '\''\1'ext1\'' '\''\1'ext2\''/g' |
sh
for better performance: only one process created
perl -le '($e,$f)=@ARGV;map{$o=$_;s/$e$/$f/;rename$o,$_}<*.$e>' ext2 ext3

- 7,973
- 2
- 29
- 50

- 18,726
- 2
- 31
- 36
well this should work
mv $file $(echo $file | sed -E -e 's/.xml.bak.*/.xml/g' | sed -E -e 's/.\///g')
output
abc.xml.bak.foobar -> abc.xml

- 485
- 9
- 19