12

I am doing a script that is installing ros and after installing it, compiling a workspace with catkin_make.

I found the solution to solve my problem but I can't explain the reason. I have a file called install.bash that is calling others:

#!/bin/bash

source 01_install_ros.bash

What is important is in 01_install_ros.bash:

# variable not set because it is done in the script setup.bash of ros
echo "before source in 01_install_ros"
echo "ROS_ROOT: "$ROS_ROOT
whereis catkin_make
echo ""

echo "source /opt/ros/kinetic/setup.bash" >> $HOME/.bashrc
# doesn't set the variables
source "$HOME"/.bashrc
# the solutions
source /opt/ros/kinetic/setup.bash

# variables not set if I use the source of .bashrc
echo "after source in 01_install_ros"
echo "ROS_ROOT: "$ROS_ROOT
whereis catkin_make
echo ""

As written in comments, sourcing .bashrc instead of directly setup.bash doesn't work. I really don't get why. Can you explain me?

onda47
  • 615
  • 2
  • 6
  • 21
  • `.bashrc` is intended to be read by the Bash itself. Why do you think it might be necessary to do it in a script? – ceving Apr 27 '17 at 13:25
  • Because I add a line at the end in `01_install_ros.bash` that is sourcing setup.bash from ros. And I need to source it. – onda47 Apr 27 '17 at 13:43
  • Because I add the line `source /opt/ros/kinetic/setup.bash" at the end of `.bashrc`. I do that in `01_install_ros.bash`. And I need to source this setup.bash to have access to catkin_make in $PATH. What I don't understand is why I don't have this access when I am sourcing .bashrc. I hope to be clear. – onda47 Apr 27 '17 at 13:50
  • 1
    Use `exec bash` to start a new Bash, which has read the updated `.bashrc`. – ceving Apr 27 '17 at 13:56
  • I think that you don't get it. I don't search for a solution. I want an explanation. Why it doesn't work with `source ~/.bashrc`? I know how to solve it: `source /opt/ros/kinetic/setup.bash` directly. – onda47 Apr 27 '17 at 14:09
  • Create a [MCVE](https://stackoverflow.com/help/mcve) or use `set -x` to find out yourself. – ceving Apr 27 '17 at 14:11
  • That's exactly what I did, copy paste the code that I put, and if you have ros installed, it will reproduce the thing... – onda47 Apr 27 '17 at 14:16
  • It's possible that `$HOME` is not set in whatever environment you're invoking it. You should do `set -e` at the start to make sure it stops at the first failure. It might reveal more. – coladict Apr 27 '17 at 14:19

1 Answers1

20

Some platforms come with a ~/.bashrc that has a conditional at the top that explicitly stops processing if the shell is found to be non-interactive - even though bash only automatically sources ~/.bashrc in interactive (non-login) sessions anyway.

For example, on Ubuntu 18.04:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac

A similar test, seen in /etc/bash.bashrc on the same platform:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

If this is the case, sourcing ~/.bashrc from a script will have no effect, because scripts run in non-interactive shells by default.

Your options are:

  • Either: deactivate the conditional in ~/.bashrc

  • Or: Try to to emulate an interactive shell before invoking source ~/.bashrc.
    The specific emulation needed depends on the specifics of the conditional, but there are two likely approaches; you may have to employ them both if you don't know ahead of time which conditional you'll encounter:

    • set -i temporarily to make $- contain i, indicating an interactive shell.
    • If you know the contents of the line that performs the interactivity test, filter it out of the ~/.bashrc using grep, and then source the result with eval (the latter should generally be avoided, but it in this case effectively provides the same functionality as sourcing).
      Note that making sure that environment variable PS1 has a value is not enough, because Bash actively resets it in non-interactive shells - see this answer for background information.
      • eval "$(grep -vFx '[ -z "$PS1" ] && return' ~/.bashrc)"

Alternatively, if you control how your own script is invoked, you can invoke it with
bash -i script.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 2
    `set -i` doesn't seem to work, and I can't modify `$-` manually either. – nupanick Feb 10 '20 at 21:10
  • Are there any downsides deactivating the conditional? – deanresin May 30 '21 at 00:48
  • 1
    @deanresin: I don't think so, given that `bash` only sources `~/.bashrc` automatically in _interactive_ (non-login) sessions anyway. – mklement0 May 30 '21 at 01:50
  • @nupanick, the answers states that `set -i` only works if `~/.bashrc` contains the conditional listed first in the answer, where the (read-only) `$_` variable is checked. If the conditional is based on `$PS1` or similar, you must use the `grep`-based technique. – mklement0 May 30 '21 at 01:53