问题
I am writing a a bash script (echoo.sh) with the intention of echoing the command before it is executed. I source this script (echoo.sh) inside .bashrc. But it does not execute for commands run in script file(tmp.sh) with the bash shebang. Below is the code I have so far
echoo.sh
#!/usr/bin/env bash
shopt -s extdebug; get_hacked () {
[ -n "$COMP_LINE" ] && return # not needed for completion
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return # not needed for prompt
local this_command=$BASH_COMMAND;
echo $this_command;
};
trap 'get_hacked' DEBUG
When I open a shell and run any command - It works. But for stuff in a script file it doesn't work.
SOME FURTHER TRIES:
- I tried sourcing the .bashrc file within the script file (tmp.sh) - didn't work.
- I sourced echoo.sh inside tmp.sh and it worked.
SO, I am trying to understand
- Why doesn't it work if I just source my script in .bashrc for stuff that runs in scripts?
- Why doesn't further try #1 work when #2 does.
AND finally what can I do such that I don't have to source echoo.sh in all script files for this to work. Can source my script in one place and change some setting such that it works in all scenarios.
回答1:
I source this script (echoo.sh) inside .bashrc. But it does not execute for commands run in script file(tmp.sh) with the bash shebang
Yes it won't because you are invoking the shell non-interactively!
The shell can be spawned either interactively or non-interactively. When bash
is invoked as an interactive login shell 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 an interactive shell that is not a login shell is started, bash
reads and executes commands from ~/.bashrc
, if that file exists.
When you run a shell script with an interpreter set, it opens a new sub-shell that is non-interactive and does not have the option -i
set in the shell options.
Looking into ~/.bashrc
closely you will find a line saying
# If not running interactively, don't do anything
[[ "$-" != *i* ]] && return
which means in the script you are calling, e.g. consider the case below which am spawning a non-interactive shell explicitly using the -c
option and -x
is just to enable debug mode
bash -cx 'source ~/.bashrc'
+ source /home/foobaruser/.bashrc
++ [[ hxBc != *i* ]]
++ return
which means the rest of the the ~/.bashrc
was not executed because of this guard. But there is one such option to use here to read a start-up file for such non-interactive cases, as defined by BASH_ENV
environment variable. The behavior is as if this line is executed
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
You can define a file and pass its value to the local environment variable
echo 'var=10' > non_interactive_startup_file
BASH_ENV=non_interactive_startup_file bash -x script.sh
Or altogether run your shell script as if an interactive non login shell is spawned. Run the script with an -i
flag. Re-using the above example, with the -i
flag passed now the ~/.bashrc
file will be sourced.
bash -icx 'source ~/.bashrc'
You could also set this option when setting your interpreter she-bang in bash
to #!/bin/bash -i
So to answer your questions from the above inferences,
- Why doesn't it work if I just source my script in .bashrc for stuff that runs in scripts?
It won't because ~/.bashrc
cannot be sourced from a shell that is launched non-interactively. By-pass it by passing -i
to the script i.e. bash -i <script>
- Why doesn't further try #1 work when #2 does.
Because you are depending on reading up the ~/.bashrc
at all here. When you did source the echoo.sh
inside tmp.sh
, all its shell configurations are reflected in the shell launched by tmp.sh
来源:https://stackoverflow.com/questions/48638367/shopt-s-extdebug-in-bashrc-not-working-in-script-files