Bash: Как определить, открыт ли терминал сторонним приложением

9

Я хочу, чтобы мой bash-скрипт (в частности, мой ~/.bashrc) делал что-то, только если терминал был открыт мной непосредственно, и делал что-то еще, если он был открыт через приложение, например VS Code. Как я могу определить, в чем дело? Есть переменная для этого? Заранее спасибо.

Бумажный пакет
источник
1
В некотором роде, мой первый ответ будет идти со вторым примером в askubuntu.com/a/1042727/295286 . Попробуйте открыть VS и запустить envкоманду. Посмотрите, есть ли переменная, специфичная для VS, которую мы можем использовать.
Сергей Колодяжный
1
Если ничего нет, попробуйте наоборот: посмотрите, не устанавливает ли ваш эмулятор терминала переменную. Я использую yakuakeи имею PULSE_PROP_OVERRIDE_application.name=Yakuakeнабор переменных , и xtermустанавливает XTERM_VERSION=XTerm(322)на моей машине.
десерт
@SergiyKolodyazhnyy Не могли бы вы написать ответ для подхода к переменной среды, пожалуйста?
десерт
@ Dessert Я бы сказал, но VS не установлен, ни OP не ответил, если есть какая-то конкретная переменная окружения, к которой мы можем привязаться.
Сергей Колодяжный
@SergiyKolodyazhnyy Я тоже, но название вопроса говорит о стороннем приложении, и я полагаю, что оно работает как любой эмулятор терминала - я думаю, что ответ, как env >env_term1в одном эмуляторе, env >env_term2во втором и как использовать то, что diff env_term{1,2}говорит, очень полезен. В конце концов, OP говорит, например, VS Code .
десерт

Ответы:

10

Возможно, вы могли бы сделать это, пройдя назад по наследству оболочки и выяснив, была ли она запущена чем-то, что приравнивается к «вам», или другой программой.

Получите PID оболочки (идентификатор процесса) и, следовательно, его PPID (идентификатор родительского процесса). Продолжайте идти вверх, пока не доберетесь до чего-то, что скажет вам, откуда это произошло. Возможно, вам придется экспериментировать на вашей системе - по крайней мере, я не знаю, будет ли она универсальной.

Например, в моей системе получите PID оболочки и используйте, psчтобы показать, что это bash:

$ echo $$
18852
$ ps --pid 18852
  PID TTY          TIME CMD
18852 pts/1    00:00:00 bash

Получить PPID 18852:

$ ps -o ppid= -p 18852
18842

Узнайте, что такое PPID (18842):

$ ps --pid 18842
  PID TTY          TIME CMD
18842 ?        00:00:02 gnome-terminal

Мы видим, что это gnome-терминал, то есть эмулятор терминала / окно терминала. Может быть, этого достаточно, если ваша оболочка, запущенная другой программой, не работает в окне эмулятора терминала.

Если это не достаточно хорошо, поднимитесь на другой уровень:

$ ps -o ppid= -p 18842
 2313
$ ps --pid 2313
  PID TTY          TIME CMD
 2313 ?        00:00:00 init

Это говорит нам о том, что gnome-terminalбыло начато init. Я подозреваю, что ваша оболочка, запущенная другой программой, будет иметь что-то другое.

Марк Смит
источник
... или, возможно, подойдя к результатуpstree -s $$
SteelDriver
9
«Это говорит нам о том, что gnome-терминал был запущен init» Я считаю маловероятным, что init запустит окна терминала. Скорее, все, что начиналось с gnome-Terminal, умерло, и gnome-Terminal было переименовано в init. Проверяя гном-терминал, кажется, это двойные вилки. Поэтому, когда он выполняется, он сначала разветвляется и убивает исходный процесс, продолжая в новом.
JoL
@JoL Честная точка зрения. Этот initпроцесс не pid 1, хотя, не уверен, что это что-то изменит.
Касперд
Большое спасибо! Мне удалось обнаружить, что ни VS Code, ни Eclipse не запускают терминал как дочерний элемент gnome-terminal. Я выполнил свою команду под, if [ $(pstree -s $$ | grep "gnome-terminal" -c) -gt 0 ]; then ...и это сработало.
PaperBag
9

Что касается кода Visual Studio, очевидно, есть способ установить дополнительные переменные среды для интегрированного терминала . Итак, настройте Visual Studio для использования этой конфигурации:

"terminal.integrated.env.linux": {
  "visual_studio": "true"
}

И внутри ~/.bashrc:

if [ -n "$visual_studio" ]; then
    # do something for Visual Studio
else
    # do something else for other types of terminal
fi

В общем, вы можете положиться на среду, данную bashпроцессу. Например, в $TERMпеременной , и запустить аналогичную if..then...else...fiветвь для [ "$TERM" = "xterm" ]или что - то еще. В каждом конкретном случае вы можете исследовать различия в среде, запустив envкаждую консоль, сохранив ее в файле, как env > output_console1.txtи diff output_console1.txt output_console2.txtв соответствии с рекомендациями десертной в комментариях .

Сергей Колодяжный
источник
$Env:varэто не синтаксис для переменных среды в Bash. Для меня это похоже на Powershell.
Дитрих Эпп
@DietrichEpp Да, я изначально изучал способы установки дополнительных переменных среды в Visual Studio, но упустил из виду, что в ответах использовалась PowerShell. Так $fooчто достаточно. Кофе, наверное, недостаточно.
Сергей Колодяжный
Для общего случая сторонних программ, которые не имеют env-настройки, вы можете установить пользовательскую переменную env в оболочке перед запуском программы. Смотри мой ответ .
Питер Кордес
2

Если вы говорите об одном конкретном стороннем приложении, используйте переменную окружения. Большинство программ передают всю среду без изменений, когда они форк + исполняют новые процессы.

Итак, запустите это приложение с пользовательским env var, который вы можете проверить . например, создайте для него псевдоним alias vs=RUNNING_FROM_VSCODE=1 VSCodeили создайте скрипт-обёртку следующим образом:

#!/bin/sh
export RUNNING_FROM_VSCODE=1
exec VSCode "$@"

Тогда по вашему .bashrc, вы можете сделать

if (($RUNNING_FROM_VSCODE)); then
   echo "started from inside VSCode"
   # RUNNING_FROM_VSCODE=0  # optional if you only want the immediate child
fi

Арифметическое утверждение bash (( ))является истинным, если выражение оценивается как ненулевое целое число (именно поэтому я использовал 1выше). Пустая строка (для не заданного env var) имеет значение false. Это хорошо для булевых переменных bash, но вы можете также легко использовать trueи проверить это с помощью традиционной POSIX

if [ "x$RUNNING_FROM_VSCODE" = "xtrue" ]; then
   echo "started from inside VSCode"
fi

Если ваше приложение в основном очищает среду для своих дочерних элементов , но все еще не $PATHизменяется, вы можете использовать это в своей оболочке:

#!/bin/sh
export PATH="$PATH:/dev/null/RUNNING_FROM_VSCODE"
exec VSCode "$@"

и проверьте его с помощью сопоставления с шаблоном, например bash, [[ "${PATH%RUNNING_FROM_VSCODE}" != "$PATH" ]]чтобы проверить, не изменяет ли суффикс из PATH.

Это должно безвредно сделать один дополнительный поиск в каталоге, когда программа ищет не найденные внешние команды. /dev/nullбезусловно, не является каталогом в любой системе, поэтому его можно использовать как фиктивный каталог, который быстро приведет к тому, ENOTDIRчто поиски PATH не найдут то, что ищут в более ранних записях PATH.

Питер Кордес
источник
Скрипты-обертки обычно разумный подход, следовательно +1. Единственным небольшим недостатком является то, что если у вас есть 3 программы, вы можете захотеть иметь 3 скрипта-обертки или один скрипт-обертку, принимающий 3 разных аргумента, что может сделать его утомительным. Тем не менее, это надежный подход.
Сергей Колодяжный
1

Вот мои 2 цента. Просто добавь это к себе .bashrc. Замените terminalsсвои любимые терминалы и командуйте своими export.

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
Zalatik
источник
Это не будет работать с моделью сервер-клиент gnome-терминала.
egmont