Если можно переключиться на xterm
, вы можете использовать следующий прием. Однако есть несколько предостережений. Как только вы решите большинство из них, решение окажется довольно сложным, см. Окончательный сценарий в конце.
xterm -e 'trap "" HUP; your-application'
После получения инструкции на закрытие от оконного менеджера xterm
отправит SIGHUP группе процессов вашего приложения и завершит работу только после того, как процесс вернется.
Предполагается, что ваше приложение не сбрасывает обработчик SIGHUP и может иметь нежелательные побочные эффекты для дочерних элементов вашего приложения.
И то, и другое кажется проблемой, если ваше приложение - tmux
.
Чтобы обойти это, вы можете сделать:
xterm -e sh -c 'bash -mc tmux <&1 & trap "" HUP; wait'
Таким образом, tmux
будет запущен в другой группе процессов, поэтому только sh
получит SIGHUP (и игнорируй это).
Теперь это не относится к tmux
, который в любом случае сбрасывает обработчик этих сигналов, но в общем случае, в зависимости от вашей реализации sh
, SIGINT, SIGQUIT сигналы и, как правило, оба будут проигнорированы для вашего приложения, поскольку этот bash
запускается как асинхронная команда из неинтерактивного sh
. Это означает, что вы не можете прервать работу своего приложения с помощью Ctrl + C или Ctrl + \ .
Это требование POSIX. Некоторые оболочки, такие как mksh
, не соблюдают его (по крайней мере, не текущие версии) или только частично, например dash
, который делает это для SIGINT, но не SIGQUIT. Итак, если mksh
доступен, вы можете сделать:
xterm -e mksh -c 'bash -mc your-application <&1 & trap "" HUP; wait'
(хотя это может не работать в будущих версиях mksh
, если они решат исправить это несоответствие).
Или, если вы не можете гарантировать, что mksh
или bash
будут доступны, или предпочитаете не полагаться на поведение, которое может измениться в будущем, вы можете выполнить их работу вручную. с perl
и, например, напишите сценарий оболочки unclosable-xterm
, например:
#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -MPOSIX -e '
$pid = fork;
if ($pid == 0) {
setpgrp or die "setpgrp: $!";
tcsetpgrp(0,getpid) or die "tcsetpgrp: $!";
exec @ARGV;
die "exec: $!";
}
die "fork: $!" if $pid < 0;
$SIG{HUP} = "IGNORE";
waitpid(-1,WUNTRACED)' "$@"
(будет называться unclosable-xterm ваше-приложение и его аргументы
) .
Другой побочный эффект заключается в том, что новая группа процессов, которую мы создаем и помещаем на передний план (с помощью bash -m
или setpgrp
+ tcsetpgrp
выше ) больше не является ведущей группой процессов сеанса, поэтому больше не является осиротевшей группой процессов (сейчас за ней ухаживает родитель ( sh
или perl
)) .
Это означает, что после нажатия Ctrl + Z этот процесс будет приостановлен. Здесь наш неосторожный родитель просто выйдет, что означает, что группа процессов получит SIGHUP (и, надеюсь, умрет).
Чтобы избежать этого, мы могли бы просто игнорировать SIGTSTP в дочернем процессе, но тогда, если ваше приложение является интерактивной оболочкой, для некоторых реализаций, таких как mksh
, yash
или rc
, Ctrl-Z также не будет работать для выполняемых ими заданий.
Или мы могли бы реализовать более осторожный родитель, который возобновляет дочерний процесс каждый раз, когда он останавливается, например:
#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -MPOSIX -e '
$pid = fork;
if ($pid == 0) {
setpgrp or die "setpgrp: $!";
tcsetpgrp(0,getpid) or die "tcsetpgrp: $!";
exec @ARGV;
die "exec: $!";
}
die "fork: $!" if $pid < 0;
$SIG{HUP} = "IGNORE";
while (waitpid(-1,WUNTRACED) > 0 && WIFSTOPPED(${^CHILD_ERROR_NATIVE})) {
kill "CONT", -$pid;
}' "$@"
Другая проблема заключается в том, что если xterm
пропал по другой причине, чем close ] из оконного менеджера, например, если xterm
отключен или теряет соединение с X-сервером (из-за xkill
, действие уничтожить вашего оконного менеджера или, например, сбой X-сервера), то эти процессы не умрут, поскольку в этих случаях для их завершения также будет использоваться SIGHUP.Чтобы обойти это, вы можете использовать poll ()
на оконечном устройстве (которое будет отключено при выходе xterm
):
#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -w -MPOSIX -MIO::Poll -e '
$pid = fork; # start the command in a child process
if ($pid == 0) {
setpgrp or die "setpgrp: $!"; # new process group
tcsetpgrp(0,getpid) or die "tcsetpgrp: $!"; # in foreground
exec @ARGV;
die "exec: $!";
}
die "fork: $!" if $pid < 0;
$SIG{HUP} = "IGNORE"; # ignore SIGHUP in the parent
$SIG{CHLD} = sub {
if (waitpid(-1,WUNTRACED) == $pid) {
if (WIFSTOPPED(${^CHILD_ERROR_NATIVE})) {
# resume the process when stopped
# we may want to do that only for SIGTSTP though
kill "CONT", -$pid;
} else {
# exit when the process dies
exit;
}
}
};
# watch for terminal hang-up
$p = IO::Poll->new;
$p->mask(STDIN, POLLERR);
while ($p->poll <= 0 || $p->events(STDIN) & POLLHUP == 0) {};
kill "HUP", -$pid;
' "$@"
Сpcregrep
:
extract_key_value() {
pcregrep -Mo1 "(?sx)
(?:
\Q$1\E # key literally
| \"\Q$1\E\" # same in double quotes
| '\Q$1\E' # same in single quotes
)
[=:]
(?| # branch reset
'(.*?)'
| \"(.*?)\"
| ([^\"'\s]+)
)"
}
-M
:многострочное соответствие (, чтобы разрешить test:'foo\nbar'
...)-o1
:вывести текст, соответствующий первой группе захвата (см. ниже о сбросе ветки ). (?sx)
:включить флаг s
(сделать так, чтобы .
также соответствовал символам новой строки )и x
флаг (разрешить это многострочное с форматом комментариев)\Q$1\E
содержание$1
(первого аргумента функции )следует понимать буквально. Это предполагает, что он не содержит сам \E
. В ksh93 -, как и в оболочках типа bash
, вы можете заменить $1
на ${1//\\E/\\E\\\\E\\Q}
, чтобы обойти это. (?|.(.).|.(.).)
сброс ответвления. Нумерация группы захвата начинается с 1 после каждого |
, поэтому -o1
вернет первую группу захвата, совпадающую в любом из чередований. '.*?'
. .*?
— это не-жадный вариант .*
, поэтому '.*'
будет соответствовать от '
до первого '
после этого. \s
:любой пробельный символ. Это не попытка решить краеугольные случаи, такие как \x
кодировки в json, встраивание кавычек в кавычки (, что делается по-разному в зависимости от языка ). Он не допускает пробелов по обе стороны от :
или =
. Все они могут быть решены в случае необходимости. Это будет зависеть от типа точного ввода, который вы пытаетесь обработать.
Пример с grep:
function extract_key_value() {
egrep -o "$1[:=]['\"[:alnum:]]+" | egrep -o "['\"[:alnum:]]+$" | egrep -o "[[:alnum:]]+"
}
echo -e "on line 1\ntest:123 asasas\non line 3\ntest='abc'\non line 5" | extract_key_value test