binit
binit

Reputation: 476

Looping file contents in bash

I have a file /tmp/a.txt whose contents I want to read in a variable many number of times. If the EOF is reached then it should start from the beginning.

i.e. If the contents of the file is "abc" and I want to get 10 chars, it should be "abcabcabca".

For this I wrote an obvious script:

while [ 1 ]; 
  do cat /tmp/a.txt; 
done | 
for i in {1..3}; 
  do read -N 10 A; 
  echo "For $i: $A"; 
done

The only problem is that it hangs! I have no idea why it does so!

I am also open to other solutions in bash.

Upvotes: 0

Views: 129

Answers (2)

u_Ltd.
u_Ltd.

Reputation: 544

The script hangs because of the first loop. After the three iterations of the second loop (for) are done, the first loop repeatedly starts new cat instances which read the file and then write the content abc to the pipe. The write to the pipe doesn't work any more in the later iterations. Yes, there is a SIGPIPE kill, but to the cat command and not to the loop itself. So the solution is to catch the error in the right place:

while [ 1 ]; 
  do cat /tmp/a.txt || break
done | 
for i in {1..3}; 
  do read -N 10 A; 
  echo "For $i: $A"; 
done

Besides: output is following:

For 1: abcabcabca
For 2: bcabcabcab
For 3: cabcabcabc
<-- (Here the shell hangs no more)

Upvotes: 0

Olivier Dulac
Olivier Dulac

Reputation: 3791

To repeat over and over a line you can :

yes "abc" | for i in {1..3}; do read -N 10 A; echo "for $i: $A"; done

yes will output 'forever', but then the for i in 1..3 will only execute the "do ... done;" part 3 times

yes add a "\n" after the string. If you don't want it, do:

 yes "abc" | tr -d '\n' | for i in {1..3}; do read -N 10 A; echo "for $i: $A"; done

In all the above, note that as the read is after a pipe, in bash it will be in a subshell, so "$A" will only available in the "do....done;" area, and be lost after!

To loop and read from a file, and also not do that in a subshell:

for i in {1..3}; do read -N 10 A ; echo "for $i: $A"; done <$(cat /the/file)

To be sure there is enough data in /the/file, repeat at will:

for i in {1..3}; do read -N 10 A ; echo "for $i: $A"; done <$(cat /the/file /the/file /the/file)

To test the latest: echo -n "abc" > /the/file (-n, so there is no trainling newline)

Upvotes: 1

Related Questions