alex
alex

Reputation: 109

Unix 'sed' command in a loop

I would like to write a script with sed command as I guess this is most suitable command for my goal. There is a file called NAME which consists of different names ordered in columns like:

STEVE
JOHN
CLARE

The script should do:

  1. Get $value from NAME file
  2. Replace $value with the constant value ('ARNOLD') by sed 's/"'$i'"/ARNOLD/g' test1.log>test2.log (test1 and test2 files are identical).

I prepared my version of script but it worked only for the latest value in the list of names:

while read i; do sed 's/'"$i"'/ARNOLD/g' test1.log >test2.log ; done<name

Upvotes: 0

Views: 2484

Answers (4)

Walter A
Walter A

Reputation: 20002

When you made a copy from your original test1.log, you can overwrite test1.log. You could use sed -i or

sed 's/'"$i"'/ARNOLD/g' test1.log >test2.log && mv test2.log test1.log

This solution requires a lot of I/O and is worse than @Ed's solution. I just added it to show how the problem @msw noticed could be fixed.

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 203502

Something like this might be what you need:

awk 'NR==FNR{names[$0];next} {for (name in names) gsub(name,"ARNOLD")}' NAME test1.log >test2.log

but without sample input and expected output it's a guess and it has the same potential problem with non-anchoring that all of the sed scripts posted so far have in that it'd, for example, change "JOHNSON" to "ARNOLDSON". WIth GNU awk you can solve that with word boundaries:

awk 'NR==FNR{names[$0];next} {for (name in names) gsub("\\<"name"\\>","ARNOLD")}' NAME test1.log >test2.log

but there may be other issues that we won't know be able to help you with until we see your sample input and expected output.

wrt using awk vs shell+sed (moved from a comment):

Every time you write a shell loop just to manipulate text you have the wrong approach. See https://unix.stackexchange.com/q/169716 for one discussion on the subject. Essentially though - UNIX shell is an environment from which to call UNIX tools with a language to sequence those calls. Awk is the tool created by the same guys who invented shell to do text processing for UNIX. So, just use it. In shell you can write scripts that LOOK like they'll do the job but it's actually incredibly difficult to get the shell syntax right to robustly do text manipulation since that's not what it's designed to do (eg. while read i is NOT how to read input in shell - you need while IFS= read -r i at least) and even if you manage to get the syntax right it's immensely slow compared to awk.

If you're going to be doing ANY text manipulation in UNIX you should get the book Effective Awk Programming, 4th Edition, by Arnold Robbins or you're going to be wasting a lot of time creating complicated, inefficient, buggy, unmaintainable and otherwise generally awful shell scripts.

Upvotes: 2

123
123

Reputation: 11216

You can use sed to generate the changes and then use the output from the first as a script for another sed to read

sed 's/.*/s\/&\/ARNOLD\/g/' NAME | sed -f- test1.log >test2.log

Explanation

sed 's/.*/s\/&\/ARNOLD\/g/'

Creates

s/STEVE/ARNOLD/g
s/JOHN/ARNOLD/g
s/CLARE/ARNOLD/g

which is then piped to

sed -f-

-f reads a script file
- is standard in from the pipe

Upvotes: 1

msw
msw

Reputation: 43487

Every time your script loops, it starts with the unmodified test1.log and changes one name. That means that only the last name changed will appear in test2.log.

Each loop un-does all prior changes.

Upvotes: 0

Related Questions