2

I have a problem with my following script (this is the relevant part of it):

#!/bin/bash

OLD=(
"_MAIN1_"
"_MAIN2_"
)

NEW=(
"#111"
"#222"
)

length=${#OLD[*]}

i=0

while (( i < length ))
do
  sed -e "s/${OLD[$i]}/${NEW[$i]}/g" oldfile.txt > newfile.txt

  #sed -e 's/_MAIN1_/#111/g' oldfile.txt > newfile.txt  # this works

  # Another way that does not work
  #sed -e 's/'"${OLD[$i]}"'/'"${NEW[$i]}"'/g' oldfile.txt > newfile.txt

  ((i++))
done

exit 0

My goal is to replace strings in a file and save it into a new one. The "old" and "new" strings are stored in an array.

I tried a lot of things and played around with single and double quotes - but nothing worked. When I echo the variables I get the correct strings inside the loop. If explicit two strings are set in sed command it works fine for this.

The string patterns follow those in my example arrays ('new' contains the underscore "_" and 'old' contains the hashtag "#").

I'm running bash on a Ubuntu 16.04 box.

Thank you very much!

  • The string patterns follow those in my example arrays ('new' contains the underscore "_" and 'old' contains the hashtag "#"). Typo? – Wildcard May 20 '17 at 3:28
  • 3
    You are not moving the edited file newfile.txt back to oldfile.txt, due to which all your edits are lost. newfile.txt would contain only the last edit at the end of the while loop. you should really be using the -i option in sed or mv newfile.txt -> oldfile.txt after every sed operation. – user218374 May 20 '17 at 4:24
2

Create a sed script that does all the substitutions, and then apply that sed script to your file.

for (( i=0; i<${#OLD[@]}; ++i )); do
        printf 's/%s/%s/g\n' "${OLD[$i]}" "${NEW[$i]}"
done >script.sed

sed -f script.sed inputfile >outputfile && mv outptufile inputfile && rm script.sed

This way you limit the number of times that you need to parse the input file to one.

For the given data in OLD and NEW the sed script will be generated as

s/_MAIN1_/#111/g
s/_MAIN2_/#222/g
  • This is a very elegant way - especially when the array contains a lot of entries. – mazienho May 21 '17 at 20:48
0

Your entire script (which doesn't work) can be replaced with the single line:

sed 's/_MAIN\([12]\)_/#\1\1\1/g' oldfile.txt > newfile.txt

Or, more readably but equivalently:

sed 's/_MAIN1_/#111/g;s/_MAIN2_/#222/g' oldfile.txt > newfile.txt

There are several dozen other ways to accomplish this, depending on your actual use case (e.g., where the data comes from, what the values really look like, what you are trying to do with the data, etc.).

  • The original array contains ~30 entries. That is why I use a loop. For my two example entries you are right. Thank you anyway! – mazienho May 20 '17 at 8:46
  • @mazienho so, write a Sed script file. – Wildcard May 20 '17 at 13:38
-1

Thanks to Rakesh Sharma's comment I could solve this by using the -i flag in sed command. Before the loop the original file is backuped:

cp oldfile.txt oldfile.backup
while (( i < length ))
do
  sed -i -e 's/'"${OLD[$i]}"'/'"${NEW[$i]}"'/g' oldfile.txt
  ((i++))
done

The next time I will use Perl for string manipulations.

Edit: Added ((i++)) to prevent an infinite loop (Thanks to Kusalananda's comment).

  • The variable i is never changed in the loop. This is an infinite loop. – Kusalananda May 20 '17 at 10:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.