How to Use Sed to Change File Extensions

Changing file extensions with sed

sed is for manipulating the contents of files, not the filename itself.

Option 1, taken from this answer by John Smith:

filename="file.ext1"
mv "${filename}" "${filename/%ext1/ext2}"

Option 2, taken from this answer by chooban:

rename 's/\.ext/\.newext/' ./*.ext

Option 3, taken from this answer by David W.:

$ find . -name "*.ext1" -print0 | while read -d $'\0' file
do
mv $file "${file%.*}.ext2"
done

and more is here.

UPDATE : (in comment asked what % and {} doing?)

"${variable}othter_chars" > if you want expand a variable in string you can use it. and %.* in {} means take the value of variable strip off the pattern .* from the tail of the value for example if your variable be filename.txt "${variable%.*} return just filename.

changing file extensions using sed command in linux

Using sed

sed -n 's/\(^[^.]*\.\)[jJ][pP]e\?[gG]/mv & \1jpg/p' <(find ~/cw/)

If the dry run indeed changes the files to the expected extension, then you can then execute the command within the sed command

sed -n 's/\(^[^.]*\.\)[jJ][pP]e\?[gG]/mv & \1jpg/pe' <(find ~/cw/)

Shell using sed to remove file extension from find results

You are only missing the close / on sed.

find . -type f -name "*.sh" -exec basename {} \;  | sed "s/\.sh//"

Recursively change file extensions in Bash

Use:

find . -name "*.t1" -exec bash -c 'mv "$1" "${1%.t1}".t2' - '{}' +

If you have rename available then use one of these:

find . -name '*.t1' -exec rename .t1 .t2 {} +
find . -name "*.t1" -exec rename 's/\.t1$/.t2/' '{}' +

Change multiple files

Better yet:

for i in xa*; do
sed -i 's/asd/dfg/g' $i
done

because nobody knows how many files are there, and it's easy to break command line limits.

Here's what happens when there are too many files:

# grep -c aaa *
-bash: /bin/grep: Argument list too long
# for i in *; do grep -c aaa $i; done
0
... (output skipped)
#

sed edit file in place

The -i option streams the edited content into a new file and then renames it behind the scenes, anyway.

Example:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

while on macOS you need:

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

Using sed to mass rename files

First, I should say that the easiest way to do this is to use the
prename or rename commands.

On Ubuntu, OSX (Homebrew package rename, MacPorts package p5-file-rename), or other systems with perl rename (prename):

rename s/0000/000/ F0000*

or on systems with rename from util-linux-ng, such as RHEL:

rename 0000 000 F0000*

That's a lot more understandable than the equivalent sed command.

But as for understanding the sed command, the sed manpage is helpful. If
you run man sed and search for & (using the / command to search),
you'll find it's a special character in s/foo/bar/ replacements.

  s/regexp/replacement/
Attempt to match regexp against the pattern space. If success‐
ful, replace that portion matched with replacement. The
replacement may contain the special character & to refer to that
portion of the pattern space which matched, and the special
escapes \1 through \9 to refer to the corresponding matching
sub-expressions in the regexp.

Therefore, \(.\) matches the first character, which can be referenced by \1.
Then . matches the next character, which is always 0.
Then \(.*\) matches the rest of the filename, which can be referenced by \2.

The replacement string puts it all together using & (the original
filename) and \1\2 which is every part of the filename except the 2nd
character, which was a 0.

This is a pretty cryptic way to do this, IMHO. If for
some reason the rename command was not available and you wanted to use
sed to do the rename (or perhaps you were doing something too complex
for rename?), being more explicit in your regex would make it much
more readable. Perhaps something like:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Being able to see what's actually changing in the
s/search/replacement/ makes it much more readable. Also it won't keep
sucking characters out of your filename if you accidentally run it
twice or something.

Awk/Sed: How to do a recursive find/replace of a string in files with a certain file extension?

chepner's helpful answer proposes the simpler and more efficient use of find's -exec action instead of piping to xargs.

Unless special xargs features are needed, this change is always worth making, and maps to xargs features as follows:

  • find ... -exec ... {} \; is equivalent to find ... -print0 | xargs -0 -n 1 ...
  • find ... -exec ... {} + is equivalent to find ... -print0 | xargs -0 ...

In other words:

  • the \; terminator invokes the target command once for each matching file/folder.

  • the + terminator invokes the target command once overall, supplying all matching file/folder paths as a single list of arguments.

    • Multiple calls happen only if the resulting command line becomes too long, which is rare, especially on Linux, where getconf ARG_MAX, the max. command-line length, is large.

Troubleshooting the OP's command:

Since the OP's xargs command passes all matching file paths at once - and per xargs defaults at the end of the command line, the resulting command will effectively look something like this:

  sed -i 's/previousword/newword/g' /myprojects/file1.cpp /myprojects/file2.cpp ...

This can easily be verified by prepending echo to sed - though (conceptual) quoting of arguments that need it (paths with, e.g., embedded spaces) will not show (note the echo):

find /myprojects -type f -name '*.cpp' -print0 | 
xargs -0 echo sed -i 's/previousword/newword/g'

Next, after running the actual command, check whether the last-modified date of the files has changed using stat:

  • If they have, yet the contents haven't changed, the implication is that sed has processed the files, but the regex in the s function call didn't match anything.

It is conceivable that older GNU sed versions don't work properly when combining -i (in-place editing) with multiple file operands (though I couldn't find anything in the GNU sed release notes).

To rule that out, invoke sed once for each file:

If you still want to use xargs, add -n 1:

 find /myprojects -type f -name '*.cpp' -print0 | 
xargs -0 -n 1 sed -i 's/previousword/newword/g'

To use find's -exec action, see chepner's answer.


With a GNU sed version that does support updating of multiple files with the -i option - which is the case as of at least v4.2.2 - the best formulation of your command is (note the quoted *.cpp argument to prevent premature expansion by the shell, and the use of terminator + to only invoke sed once):

find /myprojects -type f -name '*.cpp' -exec sed -i 's/previousword/newword/g' '{}' +


Related Topics



Leave a reply



Submit