Убивать другие процессы в конвейере (программно)

Неинтерактивные оболочки просто не имеют источника .bashrc ; это определенное поведение bash . Неинтерактивная оболочка будет источником только файла с именем переменной среды BASH_ENV . (Если запустить как sh , то вместо имени файла в качестве источника используется ENV .)

На странице руководства (выделено мной жирным шрифтом)

Когда bash вызывается как интерактивная оболочка входа в систему или как не- активная оболочка с оператором - -login сначала считывает и выполняет команды com - {1}} из файла / etc / profile , если этот файл существует. После чтения этого файла он ищет ~ / .bash_profile, ~ / .bash_login и ~ / .profile, в указанном порядке, а также читает и выполняет команды из первого файла. , что существует и доступен для чтения. Параметр --noprofile может использоваться при запуске оболочки , чтобы запретить такое поведение.

Когда оболочка входа завершается, bash читает и выполняет команды из файла ~ / .bash_logout, если он существует.

Когда запускается интерактивная оболочка , которая не является оболочкой входа в систему , bash читает и выполняет команды из ~ / .bashrc , если этот файл существует. Это можно запретить с помощью параметра --norc. Параметр файла --rcfile заставит bash читать и выполнять команды из файла вместо ~ / .bashrc.

Когда bash запускается в неинтерактивном режиме , чтобы запустить сценарий оболочки, например , он ищет переменную BASH_ENV в среде, расширяет ее значение, если он появляется там и использует расширенное значение как имя файла для чтения и выполнения. Bash ведет себя так, как если бы был выполнен следующий com - {1}} mand: if [-n "$ BASH_ENV"]; тогда . "$ BASH_ENV"; fi , но значение переменной PATH не используется для поиска файла - имя.

2
21.09.2017, 02:24
1 ответ

В:

cmd1 | cmd2

cmd2не умирает автоматически, когда cmd1завершает работу, однако он увидит конец -файла -на своем стандартном вводе. И вот так трубопроводы обычно заканчиваются.

В:

echo foo | sed s/o/e/g

sedпрекратит работу после того, как echoзавершится, потому что он обнаружит, что больше нечего читать из его стандартного ввода.

Вы можете попытаться cmd1убить cmd2после завершения, но что, если cmd2еще не прочитал все, что cmd1написал в канал?

Если cmd2умирает первым, cmd1не умирает автоматически, но он будет уничтожен (с помощью SIGPIPE )при следующей попытке записи в (теперь сломанный )канал.

Вот такие конвейеры:

yes | head

terminate(yesуничтожается при первой записи в канал после завершения headпосле чтения и печати первых 10 строк ).

Теперь, если вы действительно хотите убить процессы на другом конце канала, нет переносимого способа узнать, какой процесс (es )имеет файловый дескриптор на другом конце канала.

В Linux вы можете искать файлы в /proc/*/fd, которые имеют тот же индекс, что и канал на fd до вашего конца канала, и где разрешения самой символической ссылки указывают, какой конец канала это.

$ (ls -lLi /proc/self/fd/3; ls -l /proc/self/fd/3) 3>&1 >&2 | (ls -Lil /proc/self/fd/0; ls -l /proc/self/fd/0)
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/3|
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/0|
l-wx------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/3 -> pipe:[224052]
lr-x------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/0 -> pipe:[224052]

Один и тот же индекс канала, и fd на стороне записи имеет разрешения w, а на стороне чтения — права r.

Например,с помощью zshвы можете получить pid процессов, у которых есть fd на считывающем конце канала, другой конец которого находится на нашем fd 3 с помощью:

pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
pids=(${(u)pids}) # unique

Пример:

$ (pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
  pids=(${(u)pids}); ps -fp $pids) 3>&1 >&2 | tr a b
UID        PID  PPID  C STIME TTY          TIME CMD
chazelas 24157 11759  0 23:41 pts/1    00:00:00 tr a b

Так что вы могли бы сделать (по-прежнему в Linux и сzsh):

run_and_kill_the_other_end() {
  setopt localoptions localtraps
  trap : TERM
  "$@"
  local ret=$?
  local -aU pids
  if [ -p /dev/stdout ]; then
    pids=(/proc/<2->/fd/<0-9>(Nf{u+r}e{'[[ $REPLY -ef /dev/stdout ]]'}-p:h:h:t))
    exec >&- # give the right end a chance to see eof and act upon it
  fi
  [ -p /dev/stdin ] &&
    pids+=(/proc/<2->/fd/<0-9>(Nf{u+w}e{'[[ $REPLY -ef /dev/stdin ]]'}-p:h:h:t))
  (($#pids)) && kill $pids 2> /dev/null
  return $ret
}

А потом:

run_and_kill_the_other_end node a.js | run_and_kill_the_other_end node b.js

Хотя опять же, это, вероятно, не то, что вы хотите делать. Например:

$ run_and_kill_the_other_end seq 100 |
    run_and_kill_the_other_end sort |
    run_and_kill_the_other_end wc -l
0

sortбыл убит до того, как успел записать свой отсортированный вывод. wc -lудалось бежать.

В качестве льготного периода можно указать задержку, но какой продолжительности она должна быть? Например, если я добавлю sleep 0.1после exec >&-, я увижу:

$ run_and_kill_the_other_end seq 100 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
100
$ run_and_kill_the_other_end seq 10000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
10000
$ run_and_kill_the_other_end seq 100000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
0

Это также означает безоговорочную отсрочку закрытия конвейера. Вы можете улучшить это, используя вместо этого zshzselectс тайм-аутом.

5
27.01.2020, 21:55

Теги

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