Как я могу быстрее отказаться от процесса переднего плана в zsh?

Очевидное решение состояло бы в том, чтобы использовать разделение слова оболочки, но остерегаться нескольких глюков:

IFS=:
set -f
for dir in $PATH; do
    dir=${dir:-.}
    [ -x "${dir%/}/$1" ] && printf "%s\n" "$dir"
done

Вам нужно set -f потому что, когда переменную оставляют неупомянутой, и разделение слова и поколение имени файла (globbing) выполняются на нем, и здесь Вы только хотите разделение слова (например, в маловероятном случае, что $PATH содержит /usr/local/*bin*, Вы хотите это, действительно заглядывают /usr/local/*bin* папка, не в /usr/local/bin и /usr/local/sbin..., и если PATH содержит /*/*/*/../../../*/*/*/*/../../../*/*/*/*, Вы не хотите, чтобы это снизило Вашу машину),

Пустое $PATH компонент означает текущий каталог (.), нет /. $dir/$1 не было бы корректно в этом случае. Работа вокруг должна или записать $dir${dir:+/}$1 или измениться $dir кому: . в этом случае (который дает более полезный вывод при отображении с printf '%s\n' "$dir".

//foo не обязательно то же как /foo, итак, если / находится в $PATH, Вы не хотите $dir/$1, который был бы //$1. Следовательно ${dir%/} разделять запаздывающую наклонную черту.

Затем существует несколько других проблем:

Для $PATH, ":" разделитель полей в то время как для $IFS, это - разделитель полей (да, я знаю, S для Разделителя, вина ksh и POSIX для стандартизации ksh поведения).

Итак, если $PATH /usr/bin:/bin: (который является плохой практикой, но все еще обычно находимый), который означает "/usr/bin", "/bin" и "" (то есть, текущий каталог), в то время как разделение слова оболочки (весь POSIX окружает кроме zsh) разделит это на /usr/bin и /bin только.

Если $PATH установлен, но пуст, который означает: "посмотрите в текущем каталоге только". В то время как оболочки (включая тех, которые рассматривают $IFS как разделитель), развернет его до пустого списка.

Наконец, что не менее важно. Если $PATH сброшен, затем это имеет особое значение, которое является: посмотрите в списке поиска системного значения по умолчанию, который, к сожалению, означает что-то другое в зависимости от того, кого (что команда) Вы спрашиваете.

$ env -u PATH bash -c 'type usbipd'
usbipd is /usr/local/sbin/usbipd
$ env -u PATH ksh -c 'type usbipd'
ksh: whence: usbipd: not found

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

Обратите внимание, что POSIX оставляет поведение неуказанным когда $PATH сброшен или пуст, так не поможет Вам там. Это также означает, что то, что я сказал выше, не может относиться к некоторым прошлым, текущим или будущим системам POSIX/Unix.

Короче говоря, парсинг $PATH попытаться узнать, куда команда была бы выполнена от, - хитрый бизнес.

Существует стандартная команда для этого, которое является command:

ls_path=$(command -v ls)

Но то, что можно спросить: почему Вы хотите знать?

Теперь на восстановление IFS к ее значению по умолчанию:

oldIFS=$IFS
IFS=:
...
IFS=$oldIFS

будет работать на практике в большинстве случаев, но, как гарантируют, не будет работать POSIX.

Причина - это если $IFS был ранее сброшен, что означает поведение разделения значения по умолчанию (который находится в оболочках POSIX, разделенных на пространстве, вкладке или новой строке), после тех команд, это закончится набор, но пустой (что не означает разделения).

Другая потенциальная проблема состоит в том, если Вы обобщаете тот подход и используете его в большом количестве различных функций, затем если в ... часть выше, Вы вызываете функцию, которая делает то же самое (делает копию $IFS в $oldIFS), затем Вы собираетесь освободить оригинал $oldIFS и восстановите несправедливость $IFS.

Вместо этого Вы могли использовать подоболочки когда возможный:

(
  IFS=:
  ...
)
# only the subshell's IFS was affected, the parent still has its own IFS

Мой подход должен установить $IFS (и поворот set -f на или прочь) каждый раз мне нужно разделение слова (который редок), и не делайте обоих восстановлений предыдущего значения. Конечно, это не работает, если Ваш сценарий называет чужой код, который не следует той практике и принимает поведение разделения слова по умолчанию.

7
21.01.2016, 20:58
2 ответа

Невозможно иметь глобальную привязку клавиш для отказа от процесса переднего плана: нажатия клавиш принимаются процессом переднего плана, а не оболочкой. Вам нужно сначала приостановить его, нажав Ctrl + z , если вы хотите отказаться от него.

Однако оказывается, что есть опция zsh для ускорения отказа, а затем продолжения : с setopt AUTO_CONTINUE , disown автоматически также отправит SIGCONT .

Таким образом, вы можете перейти к C-z disown .

11
27.01.2020, 20:13

При нажатии Ctrl+Z на клемму, группа процесса на переднем плане получает сигнал SIGTSTP (в предположении, что клемма находится в режиме приготовления, а также в предположении, что клемма по умолчанию имеет привязки клавиш). Если процесс не установил обработчик сигнала SIGTSTP, это приводит к приостановке процесса (и даже если процесс установил обработчик сигнала, он обычно выполняет только небольшую обработку перед тем, как приостановить себя). Когда процесс завершается, приостанавливается или возобновляется, его родитель получает уведомление по сигналу SIGCHLD; это приводит к тому, что оболочка отображает новое приглашение и, возможно, сообщение типа [1] + 1234 suspended foo. Поток информации составляет
.     клавиша → подвеска → оболочка
а не
    ключ → оболочка → подвеска
поэтому для Ctrl+Z нельзя настроить другую реакцию.

Вы можете установить ловушку для SIGCHLD, но ловушка не выполняется, когда задание является текущим заданием на переднем плане. По большей части это означает, что ловушка выполняется только тогда, когда завершается фоновое задание, а не тогда, когда завершается задание на переднем плане. Но это также означает, что Вы не можете реагировать на приостановку задания переднего плана.

Вы можете косвенно реагировать на изменения в фоновом задании, установив крючок precmd, который сравнивает состояния задания с состояниями, сохраненными при предыдущем запуске precmd. Но я не знаю, что с этим делать: может быть только один ключ приостановки, поэтому вам нужна некоторая дополнительная информация, чтобы узнать, должен ли процесс быть приостановлен, фоновым или отключен.

Я настроил zsh так, чтобы нажатие Ctrl+Z при пустой подсказке фоном создавало текущее задание. Это позволяет мне дважды нажать Ctrl+Z, чтобы поставить задание на передний план в фоновом режиме только с короткой подвеской.

fancy-ctrl-z () {
  if [[ $#BUFFER -eq 0 ]]; then
    bg
    zle redisplay
  else
    zle push-input
  fi
}
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z

При желании можно сделать вызов disown. Или можно сделать так, чтобы третий Ctrl+Z вызвал disown со следующей непроверенной модификацией.

fancy-ctrl-z () {
  if [[ $#BUFFER -eq 0 ]]; then
    if (($fancy_ctrl_z_already_bg)); then
      disown
    else
      bg
      fancy_ctrl_z_already_bg=1
    fi
    zle redisplay
  else
    zle push-input
  fi
}
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z
fancy_ctrl_z_precmd () {
  fancy_ctrl_z_already_bg=0
}
precmd_functions+=fancy_ctrl_z_precmd
11
27.01.2020, 20:13

Теги

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