简体   繁体   中英

Looping through lines in a file in bash, without using stdin

I am foxed by the following situation.

I have a file list.txt that I want to run through line by line, in a loop, in bash. A typical line in list.txt has spaces in. The problem is that the loop contains a "read" command. I want to write this loop in bash rather than something like perl. I can't do it :-(

Here's how I would usually write a loop to read from a file line by line:

while read p; do
  echo $p
  echo "Hit enter for the next one."
  read x
done < list.txt

This doesn't work though, because of course "read x" will be reading from list.txt rather than the keyboard.

And this doesn't work either:

for i in `cat list.txt`; do
    echo $i
    echo "Hit enter for the next one."
  read x
done

because the lines in list.txt have spaces in.

I have two proposed solutions, both of which stink:

1) I could edit list.txt, and globally replace all spaces with "THERE_SHOULD_BE_A_SPACE_HERE" . I could then use something like sed, within my loop, to replace THERE_SHOULD_BE_A_SPACE_HERE with a space and I'd be all set. I don't like this for the stupid reason that it will fail if any of the lines in list.txt contain the phrase THERE_SHOULD_BE_A_SPACE_HERE (so malicious users can mess me up).

2) I could use the while loop with stdin and then in each loop I could actually launch eg a new terminal, which would be unaffected by the goings-on involving stdin in the original shell. I tried this and I did get it to work, but it was ugly: I want to wrap all this up in a shell script and I don't want that shell script to be randomly opening new windows. What would be nice, and what might somehow be the answer to this question, would be if I could figure out how to somehow invoke a new shell in the command and feed commands to it without feeding stdin to it, but I can't get it to work. For example this doesn't work and I don't really know why:

while read p; do 
  bash -c "echo $p; echo ""Press enter for the next one.""; read x;"; 
done < list.txt

This attempt seems to fail because "read x", despite being in a different shell somehow, is still seemingly reading from list.txt. But I feel like I might be close with this one -- who knows.

Help!

You must open as a different file descriptor

while read p <&3; do
    echo "$p"
    echo 'Hit enter for the next one'
    read x
done 3< list.txt

Update: Just ignore the lengthy discussion in the comments below. It has nothing to do with the question or this answer.

I would probably count lines in a file and iterate each of those using eg. sed. It is also possible to read infinitely from stdin by changing while condition to: while true; and exit reading with ctrl+c .

line=0 lines=$(sed -n '$=' in.file)
while [ $line -lt $lines ]
do
    let line++
    sed -n "${line}p" in.file
    echo "Hit enter for the next ${line} of ${lines}."
    read -s x
done

AWK is also great tool for this. Simple way to iterate through input would be like:

awk '{ print  $0; printf "%s", "Hit enter for the next"; getline < "-" }' file

As an alternative, you can read from stderr, which by default is connected to the tty as well. The following then also includes a test for that assumption:

(
tty -s <& 2|| exit 1
while read -r line; do
    echo "$line"
    echo 'Hit enter'
    read x <& 2
done < file
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM