using sed and pstree to display the type of termin

2020-05-03 00:52发布

问题:

I've been trying to display the type of terminal being used as the name only. For instance if I was using konsole it would display konsole. So far I've been using this command.

pstree -A -s $$

That outputs this.

systemd---konsole---bash---pstree

I have the following that can extract konsole from that line

pstree -A -s $$ | sed 's/systemd---//g;s/---.*//g' | head -1

and that outputs konsole properly. But some people have output from just the pstree command that can look like this.

systemd---kdeinit4---terminator---bash---pstree

or this

systemd---kdeinit4---lxterminal---bash---pstree

and then when I add the sed command it extracts kdeinit4 instead of terminator. I can think of a couple scenarios to extract the type of terminal but none that don't contain conditional statements to check for specific types of terminals. The problem I'm having is I can't accurately predict how many non or non-relative things may be infront or behind of the terminal name or what they will be nor can I accurately predict what the terminal name will be. Does anyone have any ideas on a solution to this?

回答1:

You could use

ps -p "$PPID" -o comm=

Or

ps -p "$PPID" -o fname=

If your shell does not have PPID variable set you could get it with

ps -p "$(ps -p "$$" -o ppid= | sed 's|\s\+||')" -o fname=

Another theory is that the parent process of the current shell that doesn't belong to the same tty as the shell could actually be the one that produces the virtual terminal, so we could find it like this as well:

#!/bin/bash

shopt -s extglob

SHELLTTY=$(exec ps -p "$$" -o tty=)
P=$$

while read P < <(exec ps -p "$P" -o ppid=) && [[ $P == +([[:digit:]]) ]]; do
    if read T < <(exec ps -p "$P" -o tty=) && [[ $T != "$SHELLTTY" ]]; then
        ps -p "$P" -o comm=
        break
    fi
done


回答2:

I don't know how to isolate the terminal name on your system, but as a parsing exercise, and assuming the terminal is directly running your bash you could pipe the pstree output through:

 awk -F"---bash---" ' NF == 2 { count = split( $1, arr, "---" ); print arr [count]; }'

This will find the word prior to the "---bash---" which in your examples is

konsole
terminator
lxterminal

If you want different shell types, you could expand the field separator to include them like:

awk -F"---(bash|csh)---" ' NF == 2 { count = split( $1, arr, "---" ); print arr[count]; }'

Considering an imaginary line like:

systemd---imaginary---monkey---csh---pstree

the awk would find "monkey" as the terminal name as well as anything from your test set.



回答3:

No guarantees here, but I think this will work most of the time, on linux:

ps -ocomm= $(lsof -tl /proc/$$/fd/0 | grep -Fxf <(lsof -t /dev/ptmx))

A little explanation is probably in order, but see man ps, man lsof and (especially) man pts for information.

/dev/ptmx is a pseudo-tty master (on modern linux systems, and some other unix(-like) systems). A program will have one of these open if it is a terminal emulator, a telnet/ssh daemon, or some other program which needs a captive terminal (screen, for example). The emulator writes to the pseudo-tty master what it wants to "type", and reads the result from the pseudo-tty slave.

/proc/$$/fd/0 is stdin of process $$ (i.e. the shell in which the command is executed). If stdin has not been redirected, this will be a symlink to some slave pseudotty, /dev/pts/#. That is the other side of the /dev/ptmx device, and consequently all of the programs listed above which have /dev/ptmx open will also have some /dev/pts/# slave open as well. (You might think that you could use /dev/stdin or /dev/fd/0 instead of /proc/$$/fd/0, but those would be opened by lsof itself, and consequently would be its stdin; because of the way lsof is implemented, that won't work.) The -l option to lsof causes it to follow symlinks, so that will cause it to show the process which have the same pts open as the current shell.

The -t option to lsof causes it to produce "terse" output, consisting only of pids, one per line. The -Fx options to grep cause it to match strings, rather than regex, and to force a full line match; the -f FILE option causes it to accept the strings to match from FILE (which in this case is a process substitution), one per line.

Finally, ps -ocomm= prints out the "command" (chopped, by default, to 8 characters) corresponding to a pid.

In short, the command finds a list of terminal emulators and other master similar programs which have a master pseudo-tty, and a list of processes which use the pseudo-tty slave; finds the intersection between the two, and then looks up the command name for whatever results.



回答4:

curTerm=$(update-alternatives --query x-terminal-emulator | grep '^Best:')
curTerm=${curTerm##*/}

printf "%s\n" "$curTerm"

And the result is

terminator

Of course it can be different.
Now you can use $curTerm variable in your sed command.

But I am not sure if this is going to work properly with symlinks.