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?

XYZ
  • 27
  • 7
  • 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 04 '19 at 01:58

2 Answers2

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.

steeldriver
  • 78,509
  • 12
  • 109
  • 152
  • 1
    I wonder why you use 3 and 4 after the -u. Did you just choose these numbers randomly? – XYZ Mar 04 '19 at 02: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 – steeldriver Mar 04 '19 at 02:32
  • 1
    In the first solution, why do you add a space between the two – XYZ Mar 04 '19 at 02: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?](https://unix.stackexchange.com/questions/159513/what-are-the-shells-control-and-redirection-operators) – steeldriver Mar 04 '19 at 02:58
  • I've just seen the link you added. Well, they say it's bad to use a loop to process text, but in this case, we can't avoid the loop. – XYZ Mar 05 '19 at 00:04
  • 1
    @LittleG you may not be able to avoid a loop, but you can avoid a *shell* loop, as shown in [Kusalananda's answer](https://unix.stackexchange.com/a/504221/65304) – steeldriver Mar 05 '19 at 00: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
Kusalananda
  • 320,670
  • 36
  • 633
  • 936
  • You said 'Without calling an external utility' - does this mean it runs faster than calling sed? – XYZ Mar 04 '19 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 04 '19 at 21:45