ubuntuusers.de

Prüfen aus welchem Terminalprogramm mein Sript gestartet wurde

Status: Ungelöst | Ubuntu-Version: Xubuntu 16.04 (Xenial Xerus)
Antworten |

turbo112

Anmeldungsdatum:
9. Mai 2010

Beiträge: Zähle...

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Aber das greift nur solange, wie man (ich) nur ein und das gleiche Terminalprogram zur gleichen Zeit nutze. Wenn ich aber z.B. xfce4-terminal und xterm zu gleichen Zeit laufen habe, geht meine Prüfung schief. Warum ist auch klar: In der Variable steht dann "xfce4-terminal xterm" drin weil ein:

1
2
3
ps -u ${USER} | grep term
 6463 ?        00:00:05 xfce4-terminal
 9026 pts/2    00:00:00 xterm

ergibt.
Hat da jemand eine Idee wie ich das besser / geschickter lösen kann?

Danke für das mit nachdenken, eine Befragung von $Suchmaschine hat immer meine bereits eingesetzte Lösung (oder so ähnlich) ergeben.

sebix Team-Icon

Moderator, Webteam

Anmeldungsdatum:
14. April 2009

Beiträge: 5482

Mit $PPID bekommst du in der Shell die PID des Elternprozesses, hilf dir das?

1
2
> ps -q $PPID -o comm=
xfce4-terminal

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2503

Jenachdem, wie genau du es nimmst, musst du den Prozessbaum nach oben durchwandern, bis du einen Prozess findest, der nach einem bekannten Terminal aussieht. Oder ist garantiert, dass dein Skript immer direkt aus einem Terminal heraus gestartet wird? Was, wenn dein Skript mal durch ein anderes Skript aufgerufen wird? Oder von einem Editor?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash

find_parent_terminal()
{
    pid=$1
    comm=$(ps --no-headers -p $pid -o comm)
    echo Saw PID $pid, it\'s "'$comm'"
    case $comm in
        urxvt)
            echo My parent is urxvt, PID $pid
            ;;
        xiate)
            echo My parent is xiate, PID $pid
            ;;
        xterm)
            echo My parent is XTerm, PID $pid
            ;;
        *)
            ppid=$(ps --no-headers -p $pid -o ppid)
            if (( ppid == 1 ))
            then
                echo Reached PID 1, could not find a known terminal
            else
                find_parent_terminal $ppid
            fi
            ;;
    esac
}

find_parent_terminal $BASHPID

Wenn ich das aus Vim heraus starte, sieht es so aus:

Executing "./parent.sh"...

Saw PID 23998, it's 'parent.sh'
Saw PID 18475, it's 'vim'
Saw PID 18472, it's 'new_script'
Saw PID 18415, it's 'bash'
Saw PID 702, it's 'xiate'
My parent is xiate, PID 702

Press ENTER or type command to continue

Mir fällt so direkt aber keine tolle Möglichkeit ein, um zu erkennen, wann ein Prozess ein Terminal ist. Da oben habe ich jetzt nach dem „Prozessnamen“ geschaut. Eine Alternative wäre vielleicht, zu schauen, ob der Prozess „/dev/ptmx“ gerade geöffnet hat – aber wie zuverlässig das funktioniert, kann ich nicht sagen.


– edit: Das hier wirkt einigermaßen robust, so wirklich glücklich bin ich damit aber auch nicht.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash

find_parent_terminal()
{
    pid=$1
    comm=$(ps --no-headers -p $pid -o comm)

    echo Checking process $pid, "'$comm'"

    case $comm in
        sshd)
            echo Looks like I\'m running via SSH, PID $pid, "'$comm'"
            return
            ;;
        login)
            echo Looks like I\'m running on a Linux VT, PID $pid, "'$comm'"
            return
            ;;
    esac

    for fd in /proc/$pid/fd/*
    do
        if [[ "$(readlink -e -- "$fd" 2>/dev/null)" == /dev/ptmx ]]
        then
            echo Determined PID $pid as my terminal, "'$comm'"
            return
        fi
    done

    ppid=$(ps --no-headers -p $pid -o ppid)
    if (( ppid == 1 ))
    then
        echo Reached PID 1, could not find a terminal candidate
    else
        find_parent_terminal $ppid
    fi
}

find_parent_terminal $BASHPID

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17576

Wohnort: Berlin

turbo112 schrieb:

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Die Anführungsstriche sind übrigens überflüssig - $(...) packt schon ordentlich alles zusammen.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13020

turbo112 schrieb:

eigentlich habe ich eine Lösung für das o.G Problem.

Wofür brauchst Du diese Lösung denn eigentlich?

turbo112

(Themenstarter)

Anmeldungsdatum:
9. Mai 2010

Beiträge: 46

Hallo zusammen,
Ich möchte mich zunächst erst einmal für die vielen Antworten bedanken!

@ Vain
Danke für die Lösungen!!
Vorab, sie funktionieren! Aber ich muss mich damit noch eingehender befassen, damit ich meine Fragen, die ich habe, auch richtig stellen kann! Da möchte ich mich gern noch einmal zu einem späteren Zeitpunkt melden wollen. Es ist doch einiges an Hausaufgaben die Du mir gegeben hast! 😉

Ich werde mich nun im einzelnen zu den anderen Antworten äußern:

sebix schrieb:

Mit $PPID bekommst du in der Shell die PID des Elternprozesses, hilf dir das?

1
2
> ps -q $PPID -o comm=
xfce4-terminal

Als ich diese Lösung zuerst las, dachte ich oh wie einfach! Da hättest Du auch allein drauf kommen können.
Aber leider schade, am Prompt funktioniert das einwandfrei! Aber nicht wenn ich das aus einem Skript heraus aufrufe, dann bekomme ich als Antwort:

1
bash

Aber Trotzdem Danke, die Idee ist super!

user_unknown schrieb:

turbo112 schrieb:

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Die Anführungsstriche sind übrigens überflüssig - $(...) packt schon ordentlich alles zusammen.

Danke für den Hinweis! Das habe ich mir mal angewöhnt um zu erreichen, dass die Variable immer ein String ist. Das ist hier in diesem Fall natürlich schon durch die Ausgabe von AWK gegeben. Trotzdem Dankeschön!!

rklm schrieb:

turbo112 schrieb:

eigentlich habe ich eine Lösung für das o.G Problem.

Wofür brauchst Du diese Lösung denn eigentlich?

Ich versuche es mal Einfach:
Ich habe ein Skript um ssh-Verbindungen herzustellen, diese sollen immer in einem neuen Terminal-Fenster erstellt werden. Diese neuen Fenster haben im Titel von mir gewünschte Angaben damit ich schon in der Taskleiste sehen kann, mit welchem Server ich es zu tun habe.

Nun hatte ich irgendwann festgestellt, dass nicht alle Terminal-Programme die gleiche Option für den Titel nutzen (bei den die ich zur Zeit nutze). Nun brauchte ich eine Lösung um das Umzuschreiben. Dabei ist dann das folgende Konstrukt entstanden:

1
2
3
4
5
6
declare -r TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"
if [ "$TERMINAL" = "xterm" ] ; then
  title="-title"
else
  title="--title"
fi

Und letztendlich wird das benötigt um folgendes zu realisieren:

1
$TERMINAL $title "SSH mit '$rechner' $user@$ip" -e "ssh -t $user@$ip -p $port \"$befehl\""

Jetzt kam ein Freud, der auch dieses Skript nutzt, dass er gern ein zu nutzendes Terminal-Programm vorgeben möchte und das das Skript nicht mehr funktioniert, wenn zwei unterschiedliche Terminal-Programm gestartet sind. Das war mir nie aufgefallen, wohl weil ich das so auch nie mache ... 😉

Wenn gewünscht, kann ich das fertige Script hier gern einstellen, wenn ich die Fehler beseitigt habe!! 😊

Danke noch mal an alle!!

sebix Team-Icon

Moderator, Webteam

Anmeldungsdatum:
14. April 2009

Beiträge: 5482

turbo112 schrieb:

sebix schrieb:

Mit $PPID bekommst du in der Shell die PID des Elternprozesses, hilf dir das?

1
2
> ps -q $PPID -o comm=
xfce4-terminal

Als ich diese Lösung zuerst las, dachte ich oh wie einfach! Da hättest Du auch allein drauf kommen können.
Aber leider schade, am Prompt funktioniert das einwandfrei! Aber nicht wenn ich das aus einem Skript heraus aufrufe, dann bekomme ich als Antwort:

1
bash

Aber Trotzdem Danke, die Idee ist super!

Die komplizierteren Loesungen bauen ja auf einem sehr aehnlichen Prinzip auf.

user_unknown schrieb:

turbo112 schrieb:

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Die Anführungsstriche sind übrigens überflüssig - $(...) packt schon ordentlich alles zusammen.

Danke für den Hinweis! Das habe ich mir mal angewöhnt um zu erreichen, dass die Variable immer ein String ist. Das ist hier in diesem Fall natürlich schon durch die Ausgabe von AWK gegeben. Trotzdem Dankeschön!!

Nein. Wie user_unknown schon schrieb, sind die aeusseren "" wegen $() ueberfluessig. Der Parameter fuer awk ist auch ein String, das hat aber damit nichts zu tun.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17576

Wohnort: Berlin

turbo112 schrieb:

user_unknown schrieb:

turbo112 schrieb:

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Die Anführungsstriche sind übrigens überflüssig - $(...) packt schon ordentlich alles zusammen.

Danke für den Hinweis! Das habe ich mir mal angewöhnt um zu erreichen, dass die Variable immer ein String ist. Das ist hier in diesem Fall natürlich schon durch die Ausgabe von AWK gegeben. Trotzdem Dankeschön!!

Das kannst Du nicht erreichen, weil es in der Bash keine typisierten Variablen gibt, und keine Strings.

Mit Anführungsstrichen und Apostrophen kannst Du nur Whitespace und Sonderzeichen maskieren.

Einen Unterschied macht

1
2
a=foo bar
a="foo bar"

nicht aber

1
2
a=foobar
a="foobar"

Auch keinen Unterschied macht:

1
2
3
4
a=$(echo a b c); echo $a
a=$(echo "a b c"); echo $a
a="$(echo a b c)"; echo $a
a="$(echo "a b c")"; echo $a
1
2
3
d=$(date)
echo $d
So 3. Jul 23:27:12 CEST 2016

Hier hast Du auch 6 Strings, wenn Strings Zeichenketten sind, die durch Whitespace voneinander getrennt sind.

Aber was bekommst Du, wenn Du

1
2
b=$d
echo $b

eingibst? Das gleiche wie bei

1
2
d=So 3. Jul 23:27:12 CEST 2016
echo $d

oder nicht?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13020

Ich hatte gerade Spass daran und habe noch einen Lösungsvorschlag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/sh

unset title tty_pid

current_pid=$$

while [ -z "$title" -a $current_pid -ne 1 ]; do
  tty_pid=$(ps --no-header -o ppid $current_pid) || exit
  command=$(basename $(ps --no-header -o comm $tty_pid)) || exit

  case "$command" in
  xfce4-terminal)
	title='--title'
	;;
  *)
	echo "No term: $tty_pid"
	;;
  esac

  current_pid=$tty_pid
done

if [ -z "$title" ]; then
  echo 'ERROR: no terminal found.' >&2
  exit 1
fi


echo "title: $title"

ChickenLipsRfun2eat Team-Icon

Anmeldungsdatum:
6. Dezember 2009

Beiträge: 12067

Hallo!

Da die Bash-Könner hier schon am Werk sind, kann ich dazu nichts weiter beitragen, was Scripte angeht.

Ich habe lediglich eine Frage zur Zielsetzung: Wieso nicht Screen verwenden? Dort lässt sich per screen -dmS NAME ein deaktivierter Screen generieren, dem man Befehle senden kann und dessen Benennung ebenfalls recht frei ist. Oder ist es zwingend nötig alles in einem separaten Terminalfenster zu sehen?

turbo112

(Themenstarter)

Anmeldungsdatum:
9. Mai 2010

Beiträge: 46

Hallo Vain,
hier wie angedroht meine Antwort.
Vieles habe ich mir durch ausprobieren und nachlesen schon selbst erklären können, sodass nun nur noch wenig bleibt.
Vain schrieb:

Jenachdem, wie genau du es nimmst, musst du den Prozessbaum nach oben durchwandern, bis du einen Prozess findest, der nach einem bekannten Terminal aussieht. Oder ist garantiert, dass dein Skript immer direkt aus einem Terminal heraus gestartet wird? Was, wenn dein Skript mal durch ein anderes Skript aufgerufen wird? Oder von einem Editor?

Wie meinst Du das hier? Ja, normalerweise wird das Ganze immer aus einem Terminal gestartet. Aus einem anderen Skript habe ich es probiert, auch das geht problemlos. Aus einem Editor? ... Ich wüsste nicht einmal wie das geht, dass müsste ich erst nachlesen! ☺

Mir fällt so direkt aber keine tolle Möglichkeit ein, um zu erkennen, wann ein Prozess ein Terminal ist. Da oben habe ich jetzt nach dem „Prozessnamen“ geschaut. Eine Alternative wäre vielleicht, zu schauen, ob der Prozess „/dev/ptmx“ gerade geöffnet hat – aber wie zuverlässig das funktioniert, kann ich nicht sagen.

Das hier habe ich nicht so wirklich verstanden, um nicht zu sagen ich habe eigentlich nichts verstanden!


– edit: Das hier wirkt einigermaßen robust, so wirklich glücklich bin ich damit aber auch nicht.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash

find_parent_terminal()
{
    pid=$1
    comm=$(ps --no-headers -p $pid -o comm)

    echo Checking process $pid, "'$comm'"

    case $comm in
        sshd)
            echo Looks like I\'m running via SSH, PID $pid, "'$comm'"
            return
            ;;
        login)
            echo Looks like I\'m running on a Linux VT, PID $pid, "'$comm'"
            return
            ;;
    esac

    for fd in /proc/$pid/fd/*
    do
        if [[ "$(readlink -e -- "$fd" 2>/dev/null)" == /dev/ptmx ]]
        then
            echo Determined PID $pid as my terminal, "'$comm'"
            return
        fi
    done

    ppid=$(ps --no-headers -p $pid -o ppid)
    if (( ppid == 1 ))
    then
        echo Reached PID 1, could not find a terminal candidate
    else
        find_parent_terminal $ppid
    fi
}

find_parent_terminal $BASHPID

Das hier habe ich so für meine Belange übernommen und angepasst! Das funktioniert prima!
Ich habe nur einen Teil aus der Funktion den ich nicht 100%tig nachvollziehen kann und mir auch nicht klar ist, wofür er gut ist!
Und zwar diesen hier:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
case $comm in
    sshd)
        echo Looks like I\'m running via SSH, PID $pid, "'$comm'"
        return
        ;;
    login)
        echo Looks like I\'m running on a Linux VT, PID $pid, "'$comm'"
        return
        ;;
esac

Ich vermute, dass das Skript nicht über eine kommende "ssh-Verbindung" ausgeführt werden soll. Aber der zweite Teil ist mir überhaupt nicht klar!

Danke noch mal für Deinen super Lösungsvorschlag!

turbo112

(Themenstarter)

Anmeldungsdatum:
9. Mai 2010

Beiträge: 46

Hallo user_unknown,
zunächst einmal, Du hast mit Deiner Ursprünglichen Aussage natürlich recht!

user_unknown schrieb:

turbo112 schrieb:

user_unknown schrieb:

turbo112 schrieb:

Hallo Zusammen,
eigentlich habe ich eine Lösung für das o.G Problem.

1
TERMINAL="$(ps -u ${USER} | grep term | awk '{ print $4 }')"

Die Anführungsstriche sind übrigens überflüssig - $(...) packt schon ordentlich alles zusammen.

Mit diese hier!!

Danke für den Hinweis! Das habe ich mir mal angewöhnt um zu erreichen, dass die Variable immer ein String ist. Das ist hier in diesem Fall natürlich schon durch die Ausgabe von AWK gegeben. Trotzdem Dankeschön!!

Das kannst Du nicht erreichen, weil es in der Bash keine typisierten Variablen gibt, und keine Strings.

Richtig, keine Frage!

Mit Anführungsstrichen und Apostrophen kannst Du nur Whitespace und Sonderzeichen maskieren.

Einen Unterschied macht

1
2
a=foo bar
a="foo bar"

nicht aber

1
2
a=foobar
a="foobar"

Auch keinen Unterschied macht:

1
2
3
4
a=$(echo a b c); echo $a
a=$(echo "a b c"); echo $a
a="$(echo a b c)"; echo $a
a="$(echo "a b c")"; echo $a
1
2
3
d=$(date)
echo $d
So 3. Jul 23:27:12 CEST 2016

Hier hast Du auch 6 Strings, wenn Strings Zeichenketten sind, die durch Whitespace voneinander getrennt sind.

Bis hier kann ich auch folgen!
Das klappt wunderbar!

Aber was bekommst Du, wenn Du

1
2
b=$d
echo $b

eingibst? Das gleiche wie bei

Das kann ich aber nicht mehr nachvollziehen!
Das oben klappt noch!

1
2
d=So 3. Jul 23:27:12 CEST 2016
echo $d

oder nicht?

Aber das hier nicht mehr!
Da kommt beim setzen der Variable folgendes:

1
2
$ d=So 3. Jul 23:27:12 CEST 2016
3.: Befehl nicht gefunden.

Was ja auch logisch ist, hier muss es dann heißen:

1
$ d="So 3. Jul 23:27:12 CEST 2016"

oder?


Womit ich aber immer wieder auf die Nase falle ist folgendes, und das kann ich nicht erklären!

1
2
3
if [ -n $var ] ; then
  ...
fi

Klappt nicht!

1
2
3
if [ -n "$var" ] ; then
  ...
fi

Wenn ich es so mache aber schon, warum?
Und wenn ich in "man test" nachlese steht da:

-n STRING
        the length of STRING is nonzero

Hier ist wieder die Rede von einem String.

Ich hoffe, dass Du nicht böse bist! Denn ich bin wirklich sehr dankbar für solche Hinweise, sie machen das leben sehr viel einfacher! Denn wenn niemand etwas sagt macht man es ja immer weiter falsch! OK, in diesem Fall funktioniert es sowohl als auch, aber dass ist meist nicht so!

Deshalb noch einmal
Herzlichen DANK!!

turbo112

(Themenstarter)

Anmeldungsdatum:
9. Mai 2010

Beiträge: 46

Hallo rklm,
rklm schrieb:

Ich hatte gerade Spass daran und habe noch einen Lösungsvorschlag:

Das finde ich wirklich super!!
Leider hatte ich, als Du geschrieben hast, schon eine fertige Lösung aus Vain's zweiten Vorschlag erarbeitet. Da hatte ich zwar noch nicht alles verstanden, aber es lief! Ich werde aber auch diesen Lösungsvorschlag ausprobieren und dann sehen was für mein Vorhaben das beste / einfachste ist!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/sh

unset title tty_pid

current_pid=$$

while [ -z "$title" -a $current_pid -ne 1 ]; do
  tty_pid=$(ps --no-header -o ppid $current_pid) || exit
  command=$(basename $(ps --no-header -o comm $tty_pid)) || exit

  case "$command" in
  xfce4-terminal)
	title='--title'
	;;
  *)
	echo "No term: $tty_pid"
	;;
  esac

  current_pid=$tty_pid
done

if [ -z "$title" ]; then
  echo 'ERROR: no terminal found.' >&2
  exit 1
fi


echo "title: $title"

Recht herzlichen Dank für Deine Bemühungen!! Ich werde berichten!
DANKE!!

turbo112

(Themenstarter)

Anmeldungsdatum:
9. Mai 2010

Beiträge: 46

Hallo ChickenLipsRfun2eat,
ChickenLipsRfun2eat schrieb:

Hallo!

Da die Bash-Könner hier schon am Werk sind, kann ich dazu nichts weiter beitragen, was Scripte angeht.

Das sind sie wirklich Bash-Könner. Danke euch allen!

Ich habe lediglich eine Frage zur Zielsetzung: Wieso nicht Screen verwenden? Dort lässt sich per screen -dmS NAME ein deaktivierter Screen generieren, dem man Befehle senden kann und dessen Benennung ebenfalls recht frei ist. Oder ist es zwingend nötig alles in einem separaten Terminalfenster zu sehen?

Na ja, dass war Ziel der Sache die jeweiligen Verbindungen in separaten Terminal-Fenstern zu haben. Dadurch kann ich schon in der Panelleiste sehen welche Verbindung sich wo befindet. Das spart sehr viel sucherrei.

Ansonsten wäre Screen wirklich eine super Variante!

Danke Dir für's mitdenken!!

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2503

turbo112 schrieb:

Vain schrieb:

Jenachdem, wie genau du es nimmst, musst du den Prozessbaum nach oben durchwandern, bis du einen Prozess findest, der nach einem bekannten Terminal aussieht. Oder ist garantiert, dass dein Skript immer direkt aus einem Terminal heraus gestartet wird? Was, wenn dein Skript mal durch ein anderes Skript aufgerufen wird? Oder von einem Editor?

Wie meinst Du das hier? Ja, normalerweise wird das Ganze immer aus einem Terminal gestartet. Aus einem anderen Skript habe ich es probiert, auch das geht problemlos. Aus einem Editor? ... Ich wüsste nicht einmal wie das geht, dass müsste ich erst nachlesen! ☺

Möglicherweise bin ich mit dem Editor etwas über das Ziel hinausgeschossen. 😉 Sowieso mutmaße ich, dass das Problem viel komplexer und facettenreicher ist, als dir vielleicht bewusst ist.

Die Problematik ist einfach, dass man nicht blind sagen kann: „Der Elternprozess meines Skriptes ist das Terminal.“ Denn im Prozessbaum können zwischen deinem Skript und dem Terminal noch einige Prozesse dazwischen liegen. Zum Beispiel die Shell – oder eben auch sowas wie ein Editor oder ein Dateimanager oder was auch immer.

Deswegen der Ansatz, den Prozessbaum so lange nach oben zu durchlaufen, bis man einen Prozess gefunden hat, der „nach Terminal aussieht“.

Mir fällt so direkt aber keine tolle Möglichkeit ein, um zu erkennen, wann ein Prozess ein Terminal ist. Da oben habe ich jetzt nach dem „Prozessnamen“ geschaut. Eine Alternative wäre vielleicht, zu schauen, ob der Prozess „/dev/ptmx“ gerade geöffnet hat – aber wie zuverlässig das funktioniert, kann ich nicht sagen.

Das hier habe ich nicht so wirklich verstanden, um nicht zu sagen ich habe eigentlich nichts verstanden!

Angenommen, im Prozessbaum gibt es folgenden Teilbaum:

systemd(1)---xiate(18430)---bash(24510)---vim(25015)---meintest.sh(25110)

Das muss man von rechts nach links lesen. Ganz rechts ist mein Testskript. Der Elternprozess davon ist Vim. Davon wiederum der Elternprozess die Bash, dann kommt xiate und dann das Initsystem (PID 1, systemd).

Über diesen Teilbaum iterieren meine Skripte aus dem anderen Posting. In jedem Iterationsschritt schauen sie sich eine der PIDs an. Die Frage ist, woran man bei gegebener PID erkennen kann, ob diese PID zu einem Terminalemulator gehört.

Mein Ansatz von oben war, sich die Dateien anzuschauen, die eine PID gerade geöffnet hat. Das kann man unter Linux zum Beispiel herausfinden, indem man sich das Verzeichnis „/proc/$pid/fd“ anschaut. Für die obige Bash (PID 24510) ist das beispielsweise:

$ l /proc/24510/fd
total 0
dr─x────── 2 void users  0 07-09 18:45 │ ./
dr─xr─xr─x 9 void users  0 07-09 18:45 │ ../
lrwx────── 1 void users 64 07-09 18:45 │ 0 -> /dev/pts/1
lrwx────── 1 void users 64 07-09 18:45 │ 1 -> /dev/pts/1
lrwx────── 1 void users 64 07-09 18:45 │ 2 -> /dev/pts/1
lrwx────── 1 void users 64 07-09 18:50 │ 255 -> /dev/pts/1

Hmm. Das hilft uns nicht weiter. Schauen wir uns stattdessen PID 18430 an:

$ l /proc/18430/fd
total 0
dr─x────── 2 void users  0 07-09 18:51 │ ./
dr─xr─xr─x 9 void users  0 07-09 08:48 │ ../
lr─x────── 1 void users 64 07-09 18:51 │ 0 -> /dev/null
lrwx────── 1 void users 64 07-09 18:51 │ 1 -> socket:[58822]
lrwx────── 1 void users 64 07-09 18:51 │ 10 -> socket:[61655]
...
lrwx────── 1 void users 64 07-09 18:51 │ 21 -> /tmp/#357852 (deleted)
lrwx────── 1 void users 64 07-09 18:51 │ 22 -> /dev/ptmx
...

Aha! Diese PID hat gerade die Datei „/dev/ptmx“ geöffnet.

Du als Benutzer merkst davon nichts, aber ein Terminalemulator benutzt „/dev/ptmx“. Ich will jetzt nicht so sehr ins Detail gehen, was der damit macht. Anscheinend halten die Emulatoren aber diese Datei offen – und das ist dann das Merkmal, das in meinem Skript darüber entscheidet, ob eine PID ein Terminalemulator ist oder nicht.

Ich habe nur einen Teil aus der Funktion den ich nicht 100%tig nachvollziehen kann und mir auch nicht klar ist, wofür er gut ist! Und zwar diesen hier:
[... case mit sshd und login ...]
Ich vermute, dass das Skript nicht über eine kommende "ssh-Verbindung" ausgeführt werden soll. Aber der zweite Teil ist mir überhaupt nicht klar!

Mit deiner Vermutung liegst du richtig, glaube ich.

Wenn man sich per SSH mit einem fremden Rechner verbindet und dort dann das Skript startet, dann gibt es natürlich im Prozessbaum dieses fremden Rechners keinen dazugehörigen Terminalemulator. Denn dieses Terminal läuft ja bei dir auf dem Rechner.

Zumindest gibt es keinen „Terminalemulator“ im Sinne eines grafischen Programms, in das der User direkt Befehle eingeben kann. Was es aber durchaus gibt, ist der SSH-Daemon, der deinem Skript auch ein „Terminal“ präsentiert (auf technischer Ebene). Dieser Fall wird mit dem von dir zitierten Code-Fetzen abgefangen.

Der andere Teil mit dem „login“ fängt einen weiteren Sonderfall ab. Das ist dann relevant, wenn man sich in der Konsole auf Strg+Alt+F1 oder ähnlich einloggt. Dort gibt es keinen Terminalemulator, der „/dev/ptmx“ benutzt – die „Linux-Konsole“ funktioniert ein bisschen anders.


So ganz top ist meine Lösung übrigens nicht. In bestimmten Konstellationen hat das Skript nicht die notwendigen Rechte, um die offenen Dateien einer PID zu begutachten. Das ist bei SSH der Fall, aber auch beim Terminalmultiplexer GNU screen (tmux und dtach scheinen kein Problem zu sein). Da ist mir kein toller Workaround eingefallen. Entweder man benutzt sudo oder baut das ins „case“ mit ein …

Mein Bauchgefühl sagt mir auch so ein bisschen, dass turbos Problem nicht so ganz eindeutig lösbar ist. Man kann nach guten Hinweisen suchen, welche PID der gesuchte Terminalemulator ist. Um sicher zu sein, müsste man aber herausfinden können, welcher Prozess (eigentlich: welche Prozesse) das Master-Gegenstück zum aktiven Pseudo-Terminal-Slave (siehe Ausgabe von „tty“) geöffnet hält (afaik macht ein Terminalemulator mit „openpty()“ ein Deskriptorenpaar auf, wovon das Slave-Ende beim Kindprozess ankommt und der Master bleibt im Emulator). Verknüpft sind beide im Kernel. Mir ist nun keine Kernel-Schnittstelle bekannt, mit der man herausfinden kann, wer/wo das Master-Ende ist. Stattdessen suchen meine Skripte halt einen Elternprozess, der irgendeinen pts-Master offen hat, und das ist dann mit recht hoher Wahrscheinlichkeit unser gesuchtes Terminal – aber eben nicht mit Sicherheit.

Antworten |