Bash - Извлечь значение ключа по имени из произвольного текста?

Если можно переключиться на 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;
  ' "$@"
0
04.02.2019, 17:01
2 ответа

С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, встраивание кавычек в кавычки (, что делается по-разному в зависимости от языка ). Он не допускает пробелов по обе стороны от :или =. Все они могут быть решены в случае необходимости. Это будет зависеть от типа точного ввода, который вы пытаетесь обработать.

4
28.01.2020, 02:14

Пример с 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
3
28.01.2020, 02:14

Теги

Похожие вопросы