Search code examples
linuxbashgradleposix

Why does running gradlew in a non-interactive bash session closes the session's stdin?


I've noticed this strange issue of scripts exiting successfully early in a CI system when using gradlew. The following steps help outline this.

  1. Create a file called script with the contents:

    ./gradlew
    echo DONE
    
  2. Get a random gradlew from somewhere

  3. Run cat script | bash

Notice that DONE never appears

AFAICT, running bash non-interactively causes the exec java blah at the end of gradlew to somehow allow java to close stdin and never allow the echo DONE to be read from the script being read in via stdin from cat. Supporting facts of this are:

  • Changing the script to ./gradlew; echo DONE will print DONE
  • Replacing ./gradlew with ./gradlew < /dev/null will print DONE

Solution

  • If you have an exec something somewhere (within gradlew in your case), you are replacing the current process image (bash) with something else (java).

    From help exec:

    exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

    So the problem is not that stdin is getting closed, what is happening is that the new process (java) will be the one reading that input ("echo DONE") and probably doing nothing with it.


    Explanation with example

    Consider this script.sh:

    #!/bin/bash
    
    echo Hello
    exec cat
    echo World
    

    If you execute it providing some input for cat:

    $ ./script.sh <<< "Nice"
    Hello
    Nice
    

    You may expect also the word World be printed on the screen... WRONG!
    Here nothing happens because anything else is executed after the exec command.

    Now, if you pipe the script to bash:

    $ cat script.sh | bash
    Hello        <- bash interpreted "echo Hello" and printed Hello
    echo World   <- cat read "echo World" and printed it (no interpertation ocurred)
    

    Here you can clearly see the process image replacement in action.