0

Basically, I am trying to exit a subshell that contains a loop. Here is the code: `

stop=0
(    # subshell start
    while true           # Loop start
    do
        sleep 1           # Wait a second
        echo 1 >> /tmp/output         # Add a line to a test file

        if [ $stop = 1 ]; then exit; fi         # This should exit the subshell if $stop is 1
    done   # Loop done

) |         # Do I need this pipe?

    while true   
    do
        zenity --title="Test" --ok-label="Stop" --cancel-label="Refresh" --text-info --filename=/tmp/output --font=couriernew        # This opens Zenity and shows the file.  It returns 0 when I click stop.

      if [ "$?" = 0 ]        # If Zenity returns 0, then
      then
         let stop=1       # This should close the subshell, and
         break        # This should close this loop
      fi
    done        # This loop end
    echo Done

This does not work. It never says Done. When I press Stop it just closes the dialog, but keeps writing to the file.

Edit: I need to be able to pass a variable from the subshell to the parent shell. However, I need to keep writing to the file and keep the Zenity dialog coming up. How would I do this?

Feldspar15523
  • 165
  • 2
  • 11

1 Answers1

3

When you spawn a subshell, it creates a subprocess of the current shell. This means that if you edit a variable in one shell, it will not be reflected in the other, because they are different processes. I suggest you send the subshell to the background and use $! to get its PID, then use that PID to kill the subshell when you're ready. That would look like this:

(                               # subshell start
    while true                  # Loop start
    do
        sleep 1                 # Wait a second
        echo 1 >> /tmp/output   # Add a line to a test file
    done                        # Loop done

) &                             # Send the subshell to the background

SUBSHELL_PID=$!                 # Get the PID of the backgrounded subshell

while true   
do
  zenity --title="Test" --ok-label="Stop" --cancel-label="Refresh" --text-info --filename=/tmp/output --font=couriernew        # This opens Zenity and shows the file.  It returns 0 when I click stop.

  if [ "$?" = 0 ]               # If Zenity returns 0, then
  then
     kill $SUBSHELL_PID         # This will kill the subshell
     break                      # This should close this loop
  fi
done                            # This loop end

echo Done
Christopher Shroba
  • 7,006
  • 8
  • 40
  • 68
  • The explicit subshell with the parentheses is useless (and probably not wanted). – gniourf_gniourf Dec 28 '16 at 21:52
  • It works with or without the parentheses; it's just a matter of sending the subshell to the background or the loop. I personally prefer the subshell because it makes it clear what is being sent to the background, as opposed to `while ......... done &`, which to me looks like `done` is being backgrounded (even though it does still work). Is there any compelling reason to omit the parens? – Christopher Shroba Dec 28 '16 at 21:56
  • If I change a variable in the subshell then will it be changed in the main? If not, then how do I send value between the two? – Feldspar15523 Dec 28 '16 at 22:01
  • The second while loop could be replaced with `until zenity ...; do :; done; kill $SUBSHELL_PID`. – rici Dec 28 '16 at 22:01
  • 1
    The reason to omit the parentheses is about efficiency: you're explicitly spawning an extraneous subshell. If you feel you need some grouping syntax, then… use the grouping syntax: replace the parentheses by curly braces `{` and `}`. – gniourf_gniourf Dec 28 '16 at 22:06
  • 1
    @Feldspar15523: No; once you have sub-shells or a pipeline (which implies sub-shells), the separate processes can't interfere with each other's variables. You have to use a different IPC mechanism to tell the different processes what to do. That might be 'create a file - and have the other process check whether the file exists', but it is hard to make sure the file is cleaned up properly. You might send a signal from one process to the other. Etc. – Jonathan Leffler Dec 28 '16 at 22:34
  • @Feldspar15523 what purpose do you have for wanting to share variables? Subshells will keep the values of variables that were set when the subshell was created, but they cannot be modified from each other after that. – Christopher Shroba Dec 28 '16 at 22:44
  • @gniourf_gniourf good point! Curly braces do seem like the way to go – Christopher Shroba Dec 28 '16 at 22:44