ocehb: (Default)
ocehb ([personal profile] ocehb) wrote2016-05-04 12:28 pm
Entry tags:

Расширенный $! в zsh

Возникла задача получения pid последнего форкнутого процесса.
Проблема: если форкается труба (cmd1 | cmd2 | cmd3), то $! возвращает pid последнего процесса в трубе, а хотелось бы получить pid первого (в общем случае любого) для последующего отстрела, если он зависнет (e.g. ssh host cat big-file | sha1sum).


Рещение влоб: $[$!-1], $[$!-2] для доступа к cmd2, cmd1 etc.
Как временное решение подойдёт, но если ядро рандомизирует выдачу pidов, то работать не будет.

Решение через хеш jobstates:

     1  # function get-last-pipe-pids () {
     2  emulate -L zsh
     3  setopt extendedglob
     4  
     5  local -a ary; local elm
     6  for elm in ${(v)jobstates}; do
     7      ary=( ${(@s.:.)elm} )
     8      [[ ${ary[2]} == "+" ]] && break
     9      ary=()
    10  done
    11  
    12  [[ $#ary == 0 ]] && return 1
    13  if [[ $# == 0 ]]; then
    14      print -l ${${ary[3,-1]}%=*}
    15  else
    16      print ${${${ary[3,-1]}%=*}[$1]}
    17  fi
    18  return
    19  
    20  # }


jobstates содержит индексированные состояния таблицы заданий:

# sleep 60 | cat | cat &
[1] 20831 20832 20833
# sleep 60 | cat | cat &
[2] 20835 20836 20837
# print -l ${(v)jobstates}
running:-:20831=running:20832=running:20833=running
running:+:20835=running:20836=running:20837=running


Поля:

  1. Статус задания (running, suspended, done)

  2. Обозначение задания ("+" -- текущее, "-" -- предидущее, пустое поля для всех остальных)

  3. Список pidов процессов в трубе с их состоянием



  • Строки 2-3 -- стандартный заголовок для zsh-скриптов

  • Строка 5 -- заводим локальные переменные

  • Строки 6-10 -- ищем в значениях (v) jobstates текущее задание (последнее из запущеных) и складываем поля, поделённые по ":" в массив ary

  • Строка 12 -- если ничего нет, то выходим с кодом ошибки

  • Строка 14 -- если аргументов нет, то печатаем pidы всех процессов (первые два поля -- состояние процесса и его обозначение нам не нужны, а состояние конкретного процесса удаляется с помощью квалификатора %=*)

  • Строка 16 -- если есть аргумент то это номер монады в трубе, и печатаем только её pid.


Пример использования:
# sleep 60 | cat | cat &
[1] 21191 21192 21193
# sleep 60 | cat | cat &
[2] 21195 21196 21197
# get-last-pipe-pids
21195
21196
21197
# get-last-pipe-pids 1
21195