Search code examples
bashshellfish

Bash script: after installation script following command isn't executed


I just want to write a small script to automate my fish shell installation. Part of that installation includes calling the official omf oh-my-fish installer script (curl ...).

#!/bin/bash

sudo apt update \
&& sudo apt install fish -y \
&& sudo chsh -s /usr/bin/fish \
&& curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish \
&& fish -c "omf install bira" \
&& echo -e "\nDone.\n"

Unfortunately, after the curl install command none of the following commands in my script are executed, it fails to continue. Does someone have an answer for this?

Update: Sorry for the inaccurate description of my problem. I've edited my question and I hope now everybody understands it better. If my description is still not good enough, please just tell me and I will edit it again.


Solution

  • If I understand you correctly, you are trying to do something like this:

    #!/usr/bin/env bash
    curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish 
    echo "Why doesn't this run?"
    echo "Do something else!"
    

    But the last two lines don't execute, right?

    Short answer

    Several solutions that I can think of:

    • Run the oh-my-fish installer with the --noninteractive flag using process substitution:

      fish <(curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install) --noninteractive
      
    • Exit the fish shell that oh-my-fish creates by either exit or Ctrl+Ctrl -- The remainder of your bash script should execute at that point.

    More Detail

    The problem is that the oh-my-fish installation script includes this "clean-up" section at the end:

    # We made it!
    say "Installation successful!"
    
    # Open a brand new shell if we are in interactive mode.
    set -q NONINTERACTIVE
      or exec fish < /dev/tty
    

    That final line does two things:

    • exec replaces the fish shell that the installer is running in with a new fish shell so that oh-my-fish is ready to use in that shell instance
    • The < /dev/tty forces that new shell to start reading input from the terminal. Without this, the script would exit, returning to your calling script. The developer of the installer doesn't want this to happen - They want the new fish instance to be "ready to go" with oh-my-fish after the installer runs.

    Regardless, that combination will prevent any "containing" script from continuing to execute until the new fish instance is terminated.

    You can see this in a bit of an MRE:

    test_exec.fish:

    #!/usr/bin/fish
    exec fish < /dev/tty
    echo "This never runs"
    

    wrapper.sh:

    #!/usr/bin/env bash
    cat test_exec.fish | fish
    echo "Why doesn't this run?"
    

    Executing wrapper.sh results in no output, since the test_exec.fish created a new fish instance that reads from the terminal.

    However, if you run ps after running ./wrapper.sh, you can see that there's more going on behind the scenes:

    $ ps
      PID TTY          TIME CMD
     1494 pts/1    00:00:00 fish
     1751 pts/1    00:00:00 bash
     1753 pts/1    00:00:00 fish
     1904 pts/1    00:00:00 ps
    

    The first fish (pid 1494, for reference here) is where you started; the bash is still running the wrapper.sh script; and the last fish is the one reading that we forced to read from /dev/tty and is now reading and executing ps.

    As mentioned above, if you exit the last fish with either exit or Ctrl+Ctrl, bash ./wrapper.sh will continue running, output:

    Why doesn't this run?
    

    It will then automatically exit the script/bash and return to the original fish (pid 1494):

    $ ps
      PID TTY          TIME CMD
     1494 pts/1    00:00:00 fish
     1958 pts/1    00:00:00 ps
    

    Note that, at least on my system, the tty redirection causes some issues, such as broken Backspace that need to be corrected with a terminal reset command.


    Alternatively, we can prevent the exec fish < /dev/tty from running at all by calling the installer with the --noninteractive flag.

    To pass a flag to the script that you are downloading, we use process substitution. This replaces:

    curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish
    

    with

    fish <(curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install) --noninteractive
    

    Note that if you were calling this from a fish script, instead of a bash script, the syntax would be:

    fish (curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | psub) --noninteractive