0

I have a problem with ksh in that a while loop is failing to obey the "while" condition. I should add now that this is ksh88 on my client's Solaris box. (That's a separate problem that can't be addressed in this forum. ;) I have seen Lance's question and some similar but none that I have found seem to address this. (Disclaimer: NO I haven't looked at every ksh question in this forum)

Here's a very cut down piece of code that replicates the problem:

  1 #!/usr/bin/ksh
  2 #
  3 go=1
  4 set -x
  5 tail -0f loop-test.txt | while [[ $go -eq 1 ]]
  6 do
  7   read lbuff
  8   set $lbuff
  9   nwords=$#
 10   printf "Line has %d words <%s>\n" $nwords "${lbuff}"
 11   if [[ "${lbuff}" = "0" ]]
 12   then
 13     printf "Line consists of %s; time to absquatulate\n" $lbuff
 14     go=0        # Violate the WHILE condition to get out of loop
 15   fi
 16 done
 17 printf "\nLooks like I've fallen out of the loop\n"
 18 exit 0

The way I test this is:

  • Run loop-test.sh in background mode
  • In a different window I run commands like "echo some nonsense >>loop_test.txt" (w/o the quotes, of course)
  • When I wish to exit, I type "echo 0 >>loop-test.txt"

What happens? It indeed sets go=0 and displays the line:

Line consists of 0; time to absquatulate

but does not exit the loop. To break out I append one more line to the txt file. The loop does NOT process that line and just falls out of the loop, issuing that "fallen out" message before exiting.

What's going on with this? I don't want to use "break" because in the actual script, the loop is monitoring the log of a database engine and the flag is set when it sees messages that the engine is shutting down. The actual script must still process those final lines before exiting.

Open to ideas, anyone?

Thanks much!

-- J.

Community
  • 1
  • 1
Jake
  • 11
  • 1
  • Not sure I really understand the problem (time for me to goto bed ;-) , but would using `continue` instead of `break` get you anything? OR feed `tail -f` output to an equivalent `awk` program? I don't understand `set $lbuff` What sort of values are going to appear in `$lbuff`? OR You could tag this with `[bash]` and get a lot more eyes on the problem. I don't think there is any ksh exclusive code here. If this is unsolved tomorrow, I'll experiment some. Good luck. – shellter Nov 17 '15 at 03:32
  • Shellter, the "set $lbuff" is just to produce the output "n words". I wanted my loop to do *something* besides just dumbly read lines. – Jake Nov 17 '15 at 03:37
  • oh yeah, also need to know output of `echo KSH_Version=${.sh.version}; uname -a`. Please edit into your question. Night-night ;-) Good luck. – shellter Nov 17 '15 at 03:42
  • Also: feeding my tail to an awk (trying to picture that! ;-) would work here but is impractical in the real monitoring script that this snippet models. – Jake Nov 17 '15 at 03:44
  • Shellter, that construct ${.sh.version} does not work in ksh88. (Client is terrified to upgrade anything.) $ uname -a SunOS 5.10 Generic_150400-23 sun4v sparc sun4v – Jake Nov 17 '15 at 03:46
  • is `/usr/xpg4/bin/ksh` installed? or maybe it's ksh93. Been quite a while since I've nad a Sun machine to work with. Bye for now. – shellter Nov 17 '15 at 03:50

2 Answers2

1

OK, that flopped pretty quick. After reading a few other posts, I found an answer given by dogbane that sidesteps my entire pipe-to-while scheme. His is the second answer to a question (from 2013) where I see neeraj is using the same scheme I'm using.

What was wrong? The pipe-to-while has always worked for input that will end, like a file or a command with a distinct end to its output. However, from a tail command, there is no distinct EOF. Hence, the while-in-a-subshell doesn't know when to terminate.

Dogbane's solution: Don't use a pipe. Applying his logic to my situation, the basic loop is:

while read line do # put loop body here done < <(tail -0f ${logfile})

No subshell, no problem.

Caveat about that syntax: There must be a space between the two < operators; otherwise it looks like a HEREIS document with bad syntax.

Er, one more catch: The syntax did not work in ksh, not even in the mksh (under cygwin) which emulates ksh93. But it did work in bash. So my boss is gonna have a good laugh at me, 'cause he knows I dislike bash.

So thanks MUCH, dogbane.

-- J

Community
  • 1
  • 1
Jake
  • 11
  • 1
  • `< <(tail -0f ${logfile})` still looks like a subshell to me. The ksh88 way to deal with this would probably be a co-process and `read -p`. When you exit the loop, you send a `kill -1` to the co-process, killing the `tail -0f`. I don't have access to ksh88 anymore, or I could've given a proper answer and a working example. – Henk Langeveld Nov 17 '15 at 20:49
0

After articulating the problem and sleeping on it, the reason for the described behavior came to me: After setting go=0, the control flow of the loop still depends on another line of data coming in from STDIN via that pipe.

And now that I have realized the cause of the weirdness, I can speculate on an alternative way of reading from the stream. For the moment I am thinking of the following solution:

  • Open the input file as STDIN (Need to research the exec syntax for that)
  • When the condition occurs, close STDIN (Again, need to research the syntax for that)

It should then be safe to use the more intuitive:while read lbuffat the top of the loop.

I'll test this out today and post the result. I'd hope someone else benefit from the method (if it works).

Jake
  • 11
  • 1