1

I am using Arch and bash and Openbox window manager.
Everything is up to date.
Openbox is configured with a file called rc.xml

Within rc.xml I typically call bash -c 'command1; command2; etc'
to connect necessary useful command sequences to hotkeys.

I have a hotkey definition within rc.xml as follows

<keybind key="1"><action name="Execute"><command>bash -c '
command1;
command2;
etc;
yad --timeout=1 --text="$pos_x x $pos_y";
'</command></action></keybind>

and this all works as expected and is fully functional except that globally defined variables $pos_x and $pos_y don't show.

Within .bashrc these are defined as

export pos_x=1000  
export pos_y=500  

and when I open a new bash shell terminal window and type
echo "$pos_x x $pos_y"
I see
1000 x 500
as expected.

But when in this new terminal window I type bash -c 'echo "$pos_x x $pos_y"'
I see nothing, because apparently the child process does not inherit global variables.

If I type bash -c 'source ~/.bashrc; echo "$pos_x x $pos_y"'
I see nothing, so sourcing .bashrc within the child process does not help.

How can I pass the global variables defined in .bashrc to the child process?

I had a look around and could nothing pointing to the answer.

0

2 Answers 2

3

The default ~/.bashrc on Arch contains these lines at the top of the file:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

This makes any non-interactive shell stop reading the file at that line. Since bash -c starts a non-interactive shell, the ~/.bashrc file is not read in its entirety and that's why your variables aren't defined.

So, you can try:

  1. Put the variable in ~/.profile instead of ~/.bashrc. This is the natural place for global variables anyway. As long as your login manager reads /.profile, this should work.

  2. Use a different file. Instead of putting the variable definitions in ~/.bashrc, just put them in ~/foo and then make your command bash -c '. ~/foo; echo "$pos_x x $pos_y".

  3. Put the variable definitions above the line I showed at the beginning that stops the reading of ~/.bashrc.

1
  • there's also $BASH_ENV, which, if set, tells Bash a file to read when starting to run a shell script. Of course that one isn't read for an interactive shell...
    – ilkkachu
    Oct 6, 2023 at 10:01
0

From the GNU Bash Manual, 6.2 Bash Startup Files:

When Bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. ...

When Bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute.

So, to have the environment correctly set no matter if Bash is started interactively or non-interactively, it is not sufficient to have a ~/.bash_env, you must also have the variable BASH_ENV set and pointing to it, and explicitly source it in interactive mode.

I'd put the Bash-specific stuff in Bash-specific files and leave the ~/.profile for general settings that are still valid in other shells should you one day decide to change your login shell.

First, create a ~/.bash_profile with the following contents:

 [ -f "$HOME/.profile" ] && source "$HOME/.profile"

 if [ -z "$POSIXLY_CORRECT" ]; then
     [ -z "$BASH_ENV" ] && export BASH_ENV="$HOME/.bash_env"
     source "$BASH_ENV"
 fi

Then create a file "$HOME/.bash_env" with all Bash-specific variables and functions.

Order matters: Putting the line that sources ~/.profile at the top of ~/.bash_profile enables you to override variables defined therein when running Bash.

I haven't checked whether the file referenced by the BASH_ENV variable is sourced when invoked by remote shell daemon, usually rshd, or the secure shell daemon sshd. Quoting the Bash Manual again,

If Bash determines it is being run non-interactively in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable.

it may be a good idea to also create a ~/.bashrc with the following contents:

 if [ -z "$POSIXLY_CORRECT" ]; then
     [ -z "$BASH_ENV" ] && export BASH_ENV="$HOME/.bash_env"
     source "$BASH_ENV"
 fi

In that case, you can simplify the ~/.bash_profile:

 [ -f "$HOME/.profile" ] && source "$HOME/.profile"
 [ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc"

Finally, you want to have some protection guard against multiple inclusion in your ~/.bash_env:

[ -n "$BASH_ENV_VERSION" ] && return 0
BASH_ENV_VERSION=.....

...

Note: There is no point in exporting the variable (or any variable in the whole file) because the ~/.bash_env script needs to be sourced for function definitions to become accessible in the current shell. For the same reason, none of the files that we've created needs a shebang line.

We treated interactive login-shells and non-interactive shells. An attentive reader might object that the missing piece are interactive non-login-shells. Luckily, these are covered by the steps above:

When an interactive shell that is not a login shell is started, Bash reads and executes commands from ~/.bashrc, if that file exists.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .