2

For example, the only thing in file.txt looks like this:

xxxxxxxxxHAHAxxxxxxHOHOxxxxxxx  

I hope to replace HAHA with seq 1 3 and replace HOHO with seq 5 7, so the output should be:

xxxxxxxxx1xxxxxx5xxxxxxx   
xxxxxxxxx2xxxxxx6xxxxxxx   
xxxxxxxxx3xxxxxx7xxxxxxx   

What I did:

for i in $(seq 1 3)    
do  sed "s/HAHA/$i/g" file.txt   
  for i in $(seq 5 7)    
  do sed "s/HOHO/$i/g" file.txt   
  done   
done > new.txt     

But new.txt doesn't show what I expected. How should I change the code?

1
  • By the way, I added four spaces to each line of my code, but it doesn't format like what it should be like
    – XYZ
    Mar 4, 2019 at 1:58

2 Answers 2

2

Here's one way you could do it, using the bash built-in read command's ability to read from different file descriptors:

while read -u3 i && read -u4 j; do 
    sed -e "s/HAHA/$i/" -e "s/HOHO/$j/" file.txt
done > new.txt 3< <(seq 1 3) 4< <(seq 5 7)

Since your numbers have a simple arithmetic relationship, it would be simpler to use a single seq process + some shell arithmetic:

for i in $(seq 1 3); do
  sed -e "s/HAHA/$i/" -e "s/HOHO/$((i+4))/" file.txt
done > new.txt

In either case, see Why is using a shell loop to process text considered bad practice? - the inefficiencies may not matter for the minimal example you have provided, but if you are doing anything more serious you should consider using a different approach.

6
  • 1
    I wonder why you use 3 and 4 after the -u. Did you just choose these numbers randomly?
    – XYZ
    Mar 4, 2019 at 2:27
  • 1
    @LittleG they are the first two numbers after the terminal's standard input / output / error streams (fd 0, 1, 2) that are available as file descriptors Mar 4, 2019 at 2:32
  • 1
    In the first solution, why do you add a space between the two <?
    – XYZ
    Mar 4, 2019 at 2:46
  • 1
    @LittleG iirc it's necessary in order for the shell parser to distinguish < <(command) from the here-document syntax << word, but I may be wrong about that: see for example What are the shell's control and redirection operators? Mar 4, 2019 at 2:58
  • 1
    @LittleG you may not be able to avoid a loop, but you can avoid a shell loop, as shown in Kusalananda's answer Mar 5, 2019 at 0:08
2

Without calling an external utility for each line produced:

read template <file

paste <(seq 1 3) <(seq 5 7) |
while read x y; do
    t=${template/HAHA/$x}
    t=${t/HOHO/$y}
    printf '%s\n' "$t"
done >new.txt

This first reads the line from the file called file into $template. It then constructs a two-column input for a while read loop. The input to the loop is the two columns of numbers from seq.

In the loop, some bash-specific substitutions are performed on the value of $template to replace the HAHA and HOHO string with the numbers read from paste. This creates $t which is then outputted.


Using only awk, and assuming that the input is a single line only:

awk '{
         for (i = 1; i <= 3; ++i) {
             t = $0;
             sub("HAHA",   i, t)
             sub("HOHO", 4+i, t)
             print t
         }
     }' file

A shell loop for bash, mimicking the awk code above:

read template <file

for (( i = 1; i <= 3; ++i )); do
    t=${template/HAHA/$i}
    t=${t/HOHO/$((i+4))}
    printf '%s\n' "$t"
done
2
  • You said 'Without calling an external utility' - does this mean it runs faster than calling sed?
    – XYZ
    Mar 4, 2019 at 21:11
  • 1
    @LittleG Calling an external utility, like sed, in a loop is slow in comparison to using only built-in utilities in a loop, yes.
    – Kusalananda
    Mar 4, 2019 at 21:45

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.