Очевидное решение состояло бы в том, чтобы использовать разделение слова оболочки, но остерегаться нескольких глюков:
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
на или прочь) каждый раз мне нужно разделение слова (который редок), и не делайте обоих восстановлений предыдущего значения. Конечно, это не работает, если Ваш сценарий называет чужой код, который не следует той практике и принимает поведение разделения слова по умолчанию.
Невозможно иметь глобальную привязку клавиш для отказа от процесса переднего плана: нажатия клавиш принимаются процессом переднего плана, а не оболочкой. Вам нужно сначала приостановить его, нажав Ctrl + z , если вы хотите отказаться от него.
Однако оказывается, что есть опция zsh для ускорения отказа, а затем продолжения : с setopt AUTO_CONTINUE
, disown
автоматически также отправит SIGCONT
.
Таким образом, вы можете перейти к C-z
disown
.
При нажатии 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