3

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!

2
  • The string patterns follow those in my example arrays ('new' contains the underscore "_" and 'old' contains the hashtag "#"). Typo?
    – Wildcard
    Commented May 20, 2017 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
    Commented May 20, 2017 at 4:24

3 Answers 3

3

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
1
  • This is a very elegant way - especially when the array contains a lot of entries.
    – mazienho
    Commented May 21, 2017 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.).

2
  • 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
    Commented May 20, 2017 at 8:46
  • @mazienho so, write a Sed script file.
    – Wildcard
    Commented May 20, 2017 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).

1
  • The variable i is never changed in the loop. This is an infinite loop.
    – Kusalananda
    Commented May 20, 2017 at 10:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .