I want my bash script (specifically my ~/.bashrc
) to do something only if the terminal was opened by me directly, and do something else if it was opened through an app e.g. VS Code.
How can I determine what is the case? Is there a variable for that?
Thanks in advance.
4 Answers
You could probably do it by walking back up the ancestry of the shell and working out whether it was started by something that equates to "you", or another program.
Get the shell's PID (process ID), and from that its PPID (parent process ID). Keep going up until you get to something which tells you where it came from. You may need to experiment on your system -- at least, I don't know whether it'll be universal.
For example, on my system, get the PID of a shell and use ps
to show that it's bash
:
$ echo $$
18852
$ ps --pid 18852
PID TTY TIME CMD
18852 pts/1 00:00:00 bash
Get the PPID of 18852:
$ ps -o ppid= -p 18852
18842
Find out what the PPID (18842) is:
$ ps --pid 18842
PID TTY TIME CMD
18842 ? 00:00:02 gnome-terminal
We can see it's gnome-terminal, i.e. the terminal emulator / terminal window. Maybe that's good enough for you, if your shell launched by the other program is not running in a terminal emulator window.
If it's not good enough, go up another level:
$ ps -o ppid= -p 18842
2313
$ ps --pid 2313
PID TTY TIME CMD
2313 ? 00:00:00 init
This tells us that gnome-terminal
was started by init
. I suspect your shell started by another program will have something different there.
-
... or perhaps by walking up the result of
pstree -s $$
Commented Jun 1, 2018 at 22:29 -
9"This tells us that gnome-terminal was started by init" I find it unlikely that init will start terminal windows. Rather, whatever started gnome-terminal died, and gnome-terminal was re-parented to init. Checking gnome-terminal out, it seems it double forks. So when it executes, it first forks itself and kills the original process, continuing in the new one.– JoLCommented Jun 2, 2018 at 0:24
-
@JoL Fair point. That
init
process isn't pid 1 though, not sure if that would change anything.– kasperdCommented Jun 3, 2018 at 9:20 -
Thanks a lot! I was able to detect that neither VS Code nor Eclipse run the terminal as a child of
gnome-terminal
. I executed my command underif [ $(pstree -s $$ | grep "gnome-terminal" -c) -gt 0 ]; then ...
and it worked.– PaperBagCommented Jun 3, 2018 at 10:21
As far as Visual Studio Code goes, there apparently is a way to set additional environment variables for the integrated terminal. So, set up Visual Studio to use this config:
"terminal.integrated.env.linux": {
"visual_studio": "true"
}
And within ~/.bashrc
:
if [ -n "$visual_studio" ]; then
# do something for Visual Studio
else
# do something else for other types of terminal
fi
In general, you could rely on the environment given to the bash
process. For instance, the $TERM
variable, and run a similar if..then...else...fi
branch for [ "$TERM" = "xterm" ]
or something else. On case-to-case basis, you can investigate the differences in the environment via running env
in each console, save that to file as in env > output_console1.txt
, and diff output_console1.txt output_console2.txt
as suggested by dessert in the comments.
-
$Env:var
is not the syntax for environment variables in Bash. This looks like a Powershell thing to me. Commented Jun 2, 2018 at 3:38 -
@DietrichEpp Yeah, I've originally was researching the ways to set additional environment variables in Visual Studio, but overlooked that the answers were using PowerShell. So
$foo
is enough. Coffee probably isn't enough. Commented Jun 2, 2018 at 4:05 -
For the general case of 3rd-party programs that don't have env-setting, you could set a custom env var in a wrapper before running the program. See my answer. Commented Jun 2, 2018 at 13:23
If you're talking about one specific third-party app, then use an environment variable. Most programs will pass along the entire environment unchanged when they fork+exec new processes.
So, start this app with a custom env var you can check for. e.g. make an alias for it like alias vs=RUNNING_FROM_VSCODE=1 VSCode
, or make a wrapper script like this:
#!/bin/sh
export RUNNING_FROM_VSCODE=1
exec VSCode "$@"
Then in your .bashrc
, you can do
if (($RUNNING_FROM_VSCODE)); then
echo "started from inside VSCode"
# RUNNING_FROM_VSCODE=0 # optional if you only want the immediate child
fi
A bash arithmetic statement (( ))
is true if the expression evaluates to a non-zero integer (which is why I used 1
above). The empty string (for an unset env var) is false. It's nice for bash boolean variables, but you could just as easily use true
and check for it with a traditional POSIX
if [ "x$RUNNING_FROM_VSCODE" = "xtrue" ]; then
echo "started from inside VSCode"
fi
If your app mostly clears the environment for its children, but still passes on $PATH
unchanged, you could use this in your wrapper:
#!/bin/sh
export PATH="$PATH:/dev/null/RUNNING_FROM_VSCODE"
exec VSCode "$@"
and check for it with a pattern-match like bash [[ "${PATH%RUNNING_FROM_VSCODE}" != "$PATH" ]]
to check if stripping a suffix from PATH changes it.
This should harmlessly do one extra directory lookup when the program is looking for not-found external commands. /dev/null
is definitely not a directory on any system, so it's safe to use as a bogus directory that will quickly result in ENOTDIR
if PATH searches don't find what they're looking for in earlier PATH entries.
-
Wrapper scripts are usually a sensible approach, hence +1. The only minor disadvantage is that if you have 3 programs, you may want to have 3 wrapper scripts or one wrapper script taking 3 different args, which can make it tedious. Nonetheless it's a solid approach. Commented Jun 2, 2018 at 17:34
Here's my 2 cents. Just add it to your .bashrc
. Replace terminals
with your favorite terminals and export
command with yours.
run_in_terminal(){
local parent_command="$(ps --no-headers --pid $PPID -o command | awk '{print $1;}')"
local parent="$(basename $parent_command)"
local terminals=( gnome-terminal st xterm ) # list your favorite terminal here
if [[ ${terminals[*]} =~ ${parent} ]]; then
# Your commands to run if in terminal
export MY_VAR_IN_TERMINAL="test"
fi
}
run_in_terminal
-
This wouldn't work with gnome-terminal's server-client model.– egmontCommented Jun 2, 2018 at 13:26
env
command. See if there's a VS-specific variable that we can use.yakuake
and have a variablePULSE_PROP_OVERRIDE_application.name=Yakuake
set, andxterm
setsXTERM_VERSION=XTerm(322)
on my machine.env >env_term1
in one emulator,env >env_term2
in a second one and how to use whatdiff env_term{1,2}
says is very useful. After all, OP says e.g. VS Code.