sed to rename files in bash loop

I am trying to use sed to rename all .txt files in /home/cmccabe/test . However, I am getting an error that I seems to be putting the files in a new directory s, instead of in the original. Thank you :).

bash

# rename classified
cd /home/cmccabe/test
pattern2_old="_classify"
pattern2_new="hg19multianno"

for file in *.txt
do
        newname=`printf '%s\n' "$file" | /bin/sed -e "s/$pattern2_old/$pattern2_new//"`
        /bin/mv -fv "$file" "$newname"
done
/bin/sed: -e expression #1, char 27: unknown option to `s'
�17-0000_classify.txt� -> ��
/bin/mv: cannot move �17-0000_classify.txt� to ��: No such file or directory
/bin/sed: -e expression #1, char 27: unknown option to `s'
�17-0001_classify.txt� -> ��
/bin/mv: cannot move �17-0001_classify.txt� to ��: No such file or directory

contents of /home/cmccabe/test before script executed

17-0000_classify.txt
17-0001_classify.txt
17-0002_classify.txt

contents of /home/cmccabe/test after script executed (desired output)

17-0000_hg19multiannotxt
17-0001_hg19multianno.txt
17-0002_hg19multianno.txt

You get the error because this:

sed -e "s/$pattern2_old/$pattern2_new//"

is not a valid sed -command. The s-subcommand has the form:

s/<regexp>/<regexp>/

and the last slash at the end leads to a simple syntax error.

Furthermore, your script is about as badly written as can be:

cd /home/cmccabe/test

Never do that in a script. Ideally you should use a commandline argument to provide the directory in question, but in absence of this do it this way:

dir="/home/cmccabe/test"

for file in "$dir"/* ; do
    mv $file ....
done

Secondly:

for file in *.txt
do
        newname=`printf '%s\n' "$file" | /bin/sed -e "s/$pattern2_old/$pattern2_new//"`
        /bin/mv -fv "$file" "$newname"
done

I spare you the obligatory sermon about not using backticks: you have been told this perhaps several dozen times already and i find the insistence to write bad scripts commendable. The POSIX variant of $(...) has only been developed to have something one can safely ignore.

You still may consider to invoke sed only once per run of the script instead of once per file the script encounters:

old="classify"
new="hg19multianno"

ls -1 "$dir"/*.txt |\
sed 's/\(.*\)'"$old"'\(.*\)/\1'"$old"'\2 \1'"$new"'\2/' |\
while read oldfile newfile ; do
     mv "$oldfile" "$newfile"
done

But even this is just a waste of processing power and other resources, because you can do the same with variable expansion inside the shell, without any external program:

old="classify"
new="hg19multianno"

ls -1 "$dir"/*txt |\
while read file ; do
     mv "$file" "${file/${old}/${new}}"
done

I hope this helps.

bakunin

1 Like

Thank you very much for your help and explanations :).