29

I have gnome-terminal and Guake installed. I want to give different startup messages to be displayed on these terminal emulators as they start. What code should I write in .bashrc so that this is achieved?

devav2
  • 37,290
VedVals
  • 3,651

11 Answers11

22

You can get the terminal emulator name, by grepping the parents process name. Therefore it works with every terminal emulator.

In bash, zsh, etc.:

basename "/"$(ps -o cmd -f -p $(cat /proc/$(echo $$)/stat | cut -d \  -f 4) | tail -1 | sed 's/ .*$//')

With fish-shell:

basename "/"(ps -o cmd -f -p (cat /proc/(echo %self)/stat | cut -d \  -f 4) | tail -1 | sed 's/ .*$//')
miu
  • 951
  • 9
  • 9
17

Try this:

echo $TERM

This is more authoritative, but could be messed up by your programs. However on mine, it says xterm and on ttys it says linux, which I think stands for Linux Console.

13

This function should do the job:

container() {
    pid=$$
    while true; do
        pid=$(ps -h -o ppid -p $pid 2>/dev/null)
        case $(ps -h -o comm -p $pid 2>/dev/null) in
        (gnome-terminal) echo "Running in gnome terminal";return;;
        (xterm) echo "Running in xterm";return;;
        (rxvt) echo "Running in rxvt";return;;
        (python) if [ ! -z "$(ps -h -o args -p $pid 2>/dev/null | grep guake)" ]; then echo "Running in Guake"; return; fi ;;
        esac
        [[ $(echo $pid) == 1 ]] && break
    done
}
container
jlliagre
  • 5,983
6

On many linux system echo $TERM return xterm see stazher post above.

To get actual terminal in use, do this:

1: Close every terminal instance currently running.

2: Open new terminal using your usual method.

3: Enter command as follows:

ps -o 'cmd=' -p $(ps -o 'ppid=' -p $$)

4: Return should be something like such:

lxterminal --geometry=135x20

Here is breakdown:

So: ps is "process status"

ps option -o is Display information associated with the space or comma separated list of keywords specified. Sounds complicated, but is not really. (space or comma) separated (list of keywords) specified.

So, (list of keywords) is 'cmd=' Only one keyword in list. So, just asking to display command to open terminal.

ps option -p is "by process id" Wow this is very nice option for ps. Problem is, you must pass to ps this process id. So, how to get process id? We unwrap expression $(ps -o 'ppid=' -p $$)

Here we must start to think a little deeper. I wish I invented this bash one-liner, but I did not. I think I stole it from https://wiki.archlinux.org/ somewhere, I could not find again. Those guys are awesome, but many times I cannot understand what they say to do until after much study. What we can do, is to understand it now, because I will explain.

so we know $ is expansion operator in bash. I like to think "un-wrap". So, $(foo -opt bar) will unwrap, or expand, "foo -opt bar". But in bash, single round brace (...) opens subshell.

So, $(foo -opt bar) expands "foo -opt bar" as run in daughter shell. Very weird and difficult to understand.

OK, so now we are running almost identical command again, ps -o 'ppid=' -p $$ but this time ps, process status, shows us what he can see from within daughter shell instance.

-o list of keywords, only one keyword as before, but ppid= this is asking directly for process id of parent shell!! From WITHIN DAUGHTER SHELL! Very clever, yes? I am so excited when I can understand this!

-p again, "by process id" and in bash $$ is the process id.

If you call ps -o 'ppid=' -p $$ , or any other command asking for $$ directly from the first shell, he might say pid=1, or the pid from xWindow, or from your desktop program, or you maybe get actual pid of shell. If you ask many times, you maybe get different answer each time!

But, if you invoke a daughter and ask her "Who's your Daddy" she will tell you! Very clever. I wish I could be such genius to invent this method.

stazher
  • 61
4

Use

readlink "/proc/$(cat /proc/$(echo $$)/stat|cut -d ' ' -f 4)/exe"
  • $$ is the current shell process id (bash, zsh, sh, etc)
  • /proc/[pid]/stat is status information about the current shell process
  • cut -d ' ' -f 4 will get the first 4th text split by space
  • /proc/[pid]/exe is a symlink to the executable
  • readlink will get the destination of a symlink
mekb
  • 175
3

Using pstree and awk is the easyest way:

pstree -sA $$ | awk -F "---" '{ print $2 }'

Explaining

  1. Display a tree of processes with pstree of $$ (the atual process).
  2. The pstree arguments:

    • -s: display the parents of a process
    • -A: display output in pure ASCII.
  3. The awk tool scan a pattern and -F argument is used to split the processes.

  4. Finally '{ print $2 }'tells to awk to output only the 2nd match pattern (in this case, the terminal emulator name).
3

So many complicated answer, maybe I am missing something, but this simple command does the job for me in bash:

ps -p $PPID -o comm=

UPDATE: I realized this doesn't work in a script. In a bash script it simply tells bash, so you have to go deeper:

PPPID=`cat /proc/$PPID/status | grep PPid |cut -d: -f2`
ps -p $PPPID -o comm=

This gets the parent of the parent.

gabor.zed
  • 151
0

This solution find the parent process of the running shell and than look for the parent process that is the terminal emulator:

pid=`ps -axo "pid,ppid,command" | grep $$ | head -n 1| awk '{print $2}'`; ps -axo "pid,ppid,command" | grep $pid | head -n 1 | awk '{$1=""; $2=""; print}'
0

You are correct, I only made answer for headline question, not question in body. So here you go, and Bob's yer Uncle.

I am not sure what the case switch was about, in one answer shown above. Such case switch is not needed. My ~/.bashrc script is actually only one simple line, all the echo commands are just for fun. How to explain...

Any term when starting reads ~/.bashrc , and executes whatever commands he will see in .bashrc. So, no matter which term is called, he will read .bashrc and execute the commands, so only structure needed in .bashrc would be to modify the behavior of or exclude one term or another. Desired behavior is for every term to execute same command, so case switch not needed. Terminal himself will tell you how he was called, so there is no need to differentiate.

Note (1) I did not test for guake, but works for all others mentioned in first answer by jlliagre.

Note (2) Due to formatting in markdown for wiki, you can not cut and paste as shown. You must delete each backtick, including to delete the underline characters, and add actual backtick, with no space before the ps or after the -p $$).

script for ~/.bashrc

# show welcome message for actual terminal in use
echo "Welcome.  You are attempting to use"
echo ""
echo _backtick_ps -o 'cmd=' -p $(ps -o 'ppid=' -p $$)_backtick_
echo ""
echo "Good Luck and God Speed."

This was much fun. I have added this to my own ~/.bashrc.

muru
  • 207,228
stazher
  • 61
0

if you are using bash , I thing this command will help you :

which $(ps -o 'cmd=' -p $(ps -o 'ppid=' -p $$))

Eric Wong
  • 156
0

If you were using ZSH, there is a better (faster) solution, which only uses ZSH builtins and manipulates /proc/$pid/{stat,cmdline} directly.

get-terminal-emulator() {
    if [[ $TTY = "/dev/tty"* ]]; then
        echo "linux-console"
        return
    fi
    local pid=$$ name=''
    while true; do
        proc_stat=(${(@f)$(</proc/${pid}/stat)})
        name=${proc_stat[2]//[()]/}
        case "${name}" in
            gnome-terminal|konsole|rxvt|xterm)
                echo "${name}"; return ;;
            python*)
                local cmdline=(${(@f)$(</proc/${pid}/cmdline)})
                if [[ "$cmdline" =~ "\\bguake.main\\b" ]]; then
                    echo "guake"; return
                fi
                ;;
        esac
        if test "$pid" = "1" -o "$pid" = ""; then
            echo "unknown"
            return
        fi
        pid=${proc_stat[4]}       
    done
}