Сколько у меня глубоких снарядов?

73

Задача : найти, сколько у меня глубоких снарядов.

Детали : я много открываю оболочку от vim. Построить и запустить и выйти. Иногда я забываю и открываю другой vim внутри, а затем еще одну оболочку. :(

Я хочу знать, сколько у меня глубоких снарядов, возможно, даже иметь его на экране оболочки всегда. (Я могу справиться с этой частью).

Мое решение : разобрать дерево процессов, найти vim и bash / zsh и выяснить глубину текущего процесса внутри него.

Что-то подобное уже существует? Я не мог ничего найти.

Pranay
источник
27
Является ли $SHLVLпеременная (поддерживаемая несколькими оболочками) тем, что вы ищете?
Стефан
1
Чтобы уточнить, вас не очень интересует, сколько (непосредственно вложенных) оболочек указано в SHLVL, но является ли ваша текущая оболочка потомком vim?
Джефф Шаллер
14
Кажется, это небольшая проблема XY - мой рабочий процесс - ^ Z, чтобы выйти из экземпляра vim в родительскую оболочку и fgвернуться назад, у которого нет этой проблемы.
дверная ручка
2
@ Doorknob, я тоже так делаю. но я предпочитаю этот, потому что тогда мне придется проверять «работу». и там может быть много работает время от времени на моей машине. Теперь добавьте TMUX с помощью уравнения. Это становится сложным и переполненным. Если я вызову shell внутри vim, это будет меньше разброса. (Однако я заканчиваю вносить беспорядок и, следовательно, вопрос).
Pranay
3
@ Doorknob: При всем моем уважении это похоже на ответ на вопрос: «Как мне ехать из точки А из точки В?» С предложением «Не езжай; просто возьмите Uber ». Если у пользователя есть рабочий процесс, который включает одновременное редактирование нескольких файлов, то наличие нескольких параллельно остановленных vimзаданий может быть более запутанным, чем наличие стека вложенных процессов. Кстати, я предпочитаю иметь несколько окон , так что я могу легко переходить назад и вперед, но я бы не назвал это проблемой XY только потому, что предпочитаю другой рабочий процесс.
Скотт

Ответы:

45

Когда я прочитал твой вопрос, моя первая мысль была $SHLVL. Затем я увидел, что вы хотите считать vimуровни в дополнение к уровням оболочки. Простой способ сделать это - определить функцию оболочки:

vim()  { ( ((SHLVL++)); command vim  "$@");}

Это будет автоматически и бесшумно увеличиваться при SHLVL каждом вводе vimкоманды. Вам нужно будет сделать это для каждого варианта vi/, vimкоторый вы когда-либо использовали; например,

vi()   { ( ((SHLVL++)); command vi   "$@");}
view() { ( ((SHLVL++)); command view "$@");}

Внешний набор скобок создает подоболочку, поэтому ручное изменение значения SHLVL не загрязняет текущую (родительскую) среду оболочки. Конечно, commandключевое слово используется для предотвращения вызова функций самими собой (что приведет к бесконечному циклу рекурсии). И, конечно, вы должны поместить эти определения в ваш .bashrcили другой файл инициализации оболочки.


В вышесказанном есть небольшая неэффективность. В некоторых оболочках (bash как один), если вы говорите

( cmd 1 ;  cmd 2 ;;  cmd n )

где внешняя исполняемая программа (т. е. не встроенная команда), оболочка удерживает дополнительный процесс, просто ожидая его завершения. Это (возможно) не обязательно; Преимущества и недостатки спорны. Если вы не возражаете связать немного памяти и слот процесса (и увидеть еще один процесс оболочки, чем вам нужно, когда вы делаете a ), то выполните вышеуказанное и перейдите к следующему разделу. То же самое, если вы используете оболочку, которая не мешает дополнительному процессу. Но если вы хотите избежать дополнительного процесса, первое, что нужно попробоватьcmdncmdnps

vim()  { ( ((SHLVL++)); exec vim  "$@");}

Команда execпредназначена для предотвращения затягивания процесса дополнительной оболочки.

Но есть гоча. Обработка оболочки SHLVLнесколько интуитивна: при запуске оболочки она проверяет, SHLVLустановлена ли она. Если он не установлен (или не установлен на какое-либо другое значение, кроме числа), оболочка устанавливает его на 1. Если он установлен (на число), оболочка добавляет к нему 1.

Но по этой логике, если вы говорите exec sh, ваш SHLVLдолжен идти вверх. Но это нежелательно, потому что ваш реальный уровень оболочки не увеличился. Оболочка обрабатывает это, вычитая один из, SHLVL когда вы делаете exec:

$ echo "$SHLVL"
1

$ set | grep SHLVL
SHLVL=1

$ env | grep SHLVL
SHLVL=1

$ (env | grep SHLVL)
SHLVL=1

$ (env) | grep SHLVL
SHLVL=1

$ (exec env) | grep SHLVL
SHLVL=0

Так

vim()  { ( ((SHLVL++)); exec vim  "$@");}

это стирка; он увеличивается SHLVLтолько для того, чтобы уменьшить его снова. Вы могли бы просто сказать vim, без пользы функции.

Примечание.
По словам Стефана Шазеласа (который знает все) , некоторые оболочки достаточно умны, чтобы этого не делать, если они execнаходятся в подоболочке.

Чтобы это исправить, вы бы сделали

vim()  { ( ((SHLVL+=2)); exec vim  "$@");}

Затем я увидел, что вы хотите считать vimуровни независимо от уровней оболочки. Ну, точно такой же трюк работает (ну, с незначительной модификацией):

vim() { ( ((SHLVL++, VILVL++)); export VILVL; exec vim "$@");}

(и т. д. для vi, viewи т. д.) exportНеобходим, потому что VILVLне определен как переменная окружения по умолчанию. Но это не обязательно должно быть частью функции; Вы можете просто сказать, export VILVLкак отдельная команда (в вашем .bashrc). И, как обсуждалось выше, если дополнительный процесс оболочки не является проблемой для вас, вы можете сделать command vimвместо этого exec vimи оставить в SHLVLпокое:

vim() { ( ((VILVL++)); command vim "$@");}

Личные предпочтения:
вы можете захотеть переименовать VILVLв нечто подобное VIM_LEVEL. Когда я смотрю на « VILVL», мои глаза болят; они не могут сказать, является ли это опечаткой «винила» или искаженной римской цифрой.


Если вы используете оболочку, которая не поддерживает SHLVL(например, dash), вы можете реализовать ее самостоятельно, если оболочка реализует файл запуска. Просто сделай что-то вроде

if [ "$SHELL_LEVEL" = "" ]
then
    SHELL_LEVEL=1
else
    SHELL_LEVEL=$(expr "$SHELL_LEVEL" + 1)
fi
export SHELL_LEVEL

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


Другие ответы касались вопроса о встраивании значений (переменных) среды в ваше приглашение оболочки, поэтому я не буду повторять это, особенно если вы говорите, что уже знаете, как это сделать.

Скотт
источник
1
Я несколько озадачен тем, что так много ответов предлагают выполнить внешнюю исполняемую программу, например psили pstree, когда вы можете сделать это с помощью встроенных команд оболочки.
Скотт
Этот ответ идеален. Я отметил это как решение (к сожалению, у него еще не так много голосов).
Pranay
Ваш подход удивителен, и вы используете только примитивы, что означает, что включение этого в мой .profile / .shellrc ничего не сломает. Я тащу их на любую машину, на которой работаю.
Pranay
1
Обратите внимание, что dashимеет арифметическое расширение. SHELL_LEVEL=$((SHELL_LEVEL + 1))должно быть достаточно, даже если $ SHELL_LEVEL был ранее не установлен или пуст. Только если вы должны были переноситься на оболочку Bourne, вам нужно будет прибегнуть к нему expr, но тогда вам также придется заменить $(...)на `..`. SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
Стефан
2
@Pranay, вряд ли это будет проблемой. Если злоумышленник может внедрить любой произвольный env var, то такие вещи, как PATH / LD_PRELOAD, являются более очевидным выбором, но если не проблемные переменные проходят, как, например, с sudo, настроенным без reset_env (и можно заставить bashскрипт прочитать ~ / .bashrc с помощью например, создание stdin сокета), тогда это может стать проблемой. Это много «если», но что-то нужно держать в голове (неанизированные данные в арифметическом контексте опасны)
Стефан
37

Вы можете рассчитывать столько раз, сколько вам нужно, чтобы перейти к дереву процессов, пока не найдете лидера сеанса. Как с zshна Linux:

lvl() {
  local n=0 pid=$$ buf
  until
    IFS= read -rd '' buf < /proc/$pid/stat
    set -- ${(s: :)buf##*\)}
    ((pid == $4))
  do
    ((n++))
    pid=$2
  done
  echo $n
}

Или POSIXly (но менее эффективно):

lvl() (
  unset IFS
  pid=$$ n=0
  until
    set -- $(ps -o ppid= -o sid= -p "$pid")
    [ "$pid" -eq "$2" ]
  do
    n=$((n + 1)) pid=$1
  done
  echo "$n"
)

Это даст 0 для оболочки, которая была запущена вашим эмулятором терминала или getty, и еще одну для каждого потомка.

Вам нужно сделать это только один раз при запуске. Например с:

PS1="[$(lvl)]$PS1"

в вашем ~/.zshrcили эквивалентном, чтобы иметь его в вашем приглашении.

tcshи несколько других оболочек ( zsh, ksh93, fishи , по bashкрайней мере) сохранить $SHLVLпеременную, они приращение при запуске (и декремента перед запуском следующей команды с exec(если это execне в субоболочке , если они не глючит (но многие из них))). Это только отслеживает количество вложений оболочки, но не вложений процессов. Также уровень 0 не гарантирован быть лидером сеанса.

Стефан Шазелас
источник
Да .. это или подобное. Я не хотел писать это самостоятельно, и я не хотел, чтобы кто-то писал это для меня. :(. Я надеялся на какую-то функцию в vim или shell или какой-нибудь плагин, который регулярно поддерживается. Я искал, но что-то не нашел.
Pranay
31

Используйте echo $SHLVL. Используйте принцип KISS . В зависимости от сложности вашей программы, этого может быть достаточно.

user2497
источник
2
Работает для bash, но не для dash.
АРУ
ШЛВЛ мне не помогает. Я знал об этом, и он также появился в поиске, когда я искал. :) Есть более подробная информация в вопросе.
Pranay
@Pranay Вы уверены, что сам vim не предоставляет эту информацию?
user2497
@ user2497, я немного. Это предпосылка вопроса. Я искал везде, я тоже знал о SHLVL. Я хотел -> а) быть уверенным, что такого нет. б) сделать это с наименьшим количеством зависимостей / обслуживания.
Pranay
16

Одно из возможных решений - посмотреть на результат pstree. При запуске внутри оболочки, которая была порождена изнутри vi, часть дерева, содержащая списки, pstreeдолжна показать вам, насколько вы глубоки. Например:

$ pstree <my-user-ID>
...
       ├─gnome-terminal-─┬─bash───vi───sh───vi───sh───pstree
...
Джон
источник
Да, это то, что я предложил в качестве решения (в вопросе). Я не хочу анализировать pstree, хотя :(. Это хорошо для ручного чтения, я подумывал написать программу, которая сделает это для меня, и дайте мне знать. Я не очень склонен писать парсер, если плагин / инструмент уже делает это :).
Pranay
11

Первый вариант - только глубина корпуса.

Простое решение для bash: добавьте к .bashrcследующим двум строкам (или измените текущее PS1значение):

PS1="${SHLVL} \w\$ "
export PS1

Результат:

1 ~$ bash
2 ~$ bash
3 ~$ exit
exit
2 ~$ exit
exit
1 ~$

Число в начале строки приглашения будет обозначать уровень оболочки.

Второй вариант, с вложенными уровнями vim и shell.

добавить эти строки в .bashrc

branch=$(pstree -ls $$)
vim_lvl=$(grep -o vim <<< "$branch" | wc -l)
sh_lvl=$(grep -o bash <<< "$branch" | wc -l)
PS1="v:${vim_lvl};s:$((sh_lvl - 1)):\w\$ "
export PS1

Результат:

v:0;s:1:/etc$ bash
v:0;s:2:/etc$ bash
v:0;s:3:/etc$ vim
##### do ':sh' command in the vim, shell level is increasing by 1
v:1;s:4:/etc$ vim
##### do ':sh' command in the vim, shell level is increasing by 1
v:2;s:5:/etc$ bash
v:2;s:6:/etc$

v: 1 - уровень глубины vim
s: 3 - уровень глубины оболочки

MiniMax
источник
это даст мне вложенность Bash. Это не даст мне гнезда vim. :)
Pranay
@Pranay Проверьте новое решение. Делай, что хочешь.
MiniMax
Да, это хорошее решение. Я могу добавить больше снарядов, и это будет работать :).
Pranay
8

В вопросе вы упомянули разбор pstree. Вот относительно простой способ:

bash-4.3$ pstree -Aals $$ | grep -E '^ *`-((|ba|da|k|c|tc|z)sh|vim?)( |$)'
                  `-bash
                      `-bash --posix
                          `-vi -y
                              `-dash
                                  `-vim testfile.txt
                                      `-tcsh
                                          `-csh
                                              `-sh -
                                                  `-zsh
                                                      `-bash --norc --verbose

В pstreeопции:

  • -A- ASCII-вывод для более легкой фильтрации (в нашем случае каждой команде предшествует `-)
  • -a - показать также аргументы команды, поскольку в качестве побочного эффекта каждая команда отображается в отдельной строке, и мы можем легко отфильтровать вывод, используя grep
  • -l - не обрезать длинные строки
  • -s- показать родителей выбранного процесса
    (к сожалению, не поддерживается в старых версиях pstree)
  • $$ - выбранный процесс - PID текущей оболочки
pabouk
источник
Да, я делал это в значительной степени. У меня также было что посчитать "bash" и "vim" и т. Д. Я просто не хотел поддерживать это. Также невозможно иметь много пользовательских функций, когда вам приходится переключать много виртуальных машин и иногда разрабатывать их.
Pranay
3

Это не дает точного ответа на вопрос, но во многих случаях может сделать это ненужным:

Когда вы впервые запустите свою оболочку, запустите set -o ignoreeof. Не кладите это в свой ~/.bashrc.

Сделайте привычкой вводить Ctrl-D, когда вы думаете, что находитесь в оболочке верхнего уровня и хотите быть уверенным.

Если вы не в оболочке верхнего уровня, Ctrl-D будет сигнализировать «конец ввода» текущей оболочке, и вы вернетесь на один уровень назад.

Если вы находитесь в оболочке верхнего уровня, вы получите сообщение:

Use "logout" to leave the shell.

Я использую это все время для цепных сессий SSH, чтобы упростить возврат к определенному уровню цепочки SSH. Это работает и для вложенных оболочек.

Wildcard
источник
1
Это определенно помогает, и да, это удалит много осложнений :). Я мог бы просто совместить это с принятым ответом :)). Условно настроен, поэтому мне, возможно, не придется постоянно просматривать мою подсказку.
Pranay