9

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.

  • 1
    There sort of is a way, my first response would be to go with second example in askubuntu.com/a/1042727/295286. Try opening VS and run env command. See if there's a VS-specific variable that we can use. – Sergiy Kolodyazhnyy Jun 1 '18 at 21:00
  • 1
    If there’s nothing, try it the other way around: See if your terminal emulator sets a variable. I use yakuake and have a variable PULSE_PROP_OVERRIDE_application.name=Yakuake set, and xterm sets XTERM_VERSION=XTerm(322) on my machine. – dessert Jun 1 '18 at 21:25
  • @SergiyKolodyazhnyy Would you write an answer for the environment variable approach please? – dessert Jun 1 '18 at 21:42
  • @dessert I would, but I don't have VS installed, neither OP has responded if there's any particular environment variable that we can latch onto. – Sergiy Kolodyazhnyy Jun 1 '18 at 21:44
  • @SergiyKolodyazhnyy Neither have I, but the question title says third-party app and I suppose it works just like any terminal emulator – I think an answer like env >env_term1 in one emulator, env >env_term2 in a second one and how to use what diff env_term{1,2} says is very useful. After all, OP says e.g. VS Code. – dessert Jun 1 '18 at 21:55
10

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 $$ – steeldriver Jun 1 '18 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. – JoL Jun 2 '18 at 0:24
  • @JoL Fair point. That init process isn't pid 1 though, not sure if that would change anything. – kasperd Jun 3 '18 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 under if [ $(pstree -s $$ | grep "gnome-terminal" -c) -gt 0 ]; then ... and it worked. – PaperBag Jun 3 '18 at 10:21
9

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. – Dietrich Epp Jun 2 '18 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. – Sergiy Kolodyazhnyy Jun 2 '18 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. – Peter Cordes Jun 2 '18 at 13:23
2

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. – Sergiy Kolodyazhnyy Jun 2 '18 at 17:34
1

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. – egmont Jun 2 '18 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.