Обнаружение пустой команды
Рассмотрим этот PS1
PS1='\n${_:+$? }$ '
Вот результат нескольких команд
$ [ 2 = 2 ]
0 $ [ 2 = 3 ]
1 $
1 $
Первая строка не показывает статус, как ожидалось, а следующие две строки показывают
правильный код выхода. Однако в строке 3 был нажат только Enter, поэтому мне бы хотелось, чтобы
статус уйти, как строка 1. Как я могу это сделать?
Ответы
Ответ 1
Здесь смешная, очень простая возможность: она использует escape-последовательность \#
PS1
вместе с расширением параметров (и способ Bash расширяет свое приглашение).
Управляющая последовательность \#
расширяется до номера команды команды, которая должна быть выполнена. Это увеличивается при каждом выполнении команды. Попробуйте:
$ PS1='\# $ '
2 $ echo hello
hello
3 $ # this is a comment
3 $
3 $ echo hello
hello
4 $
Теперь, каждый раз, когда появляется приглашение, Bash сначала расширяет escape-последовательности, найденные в PS1
, тогда (если задана опция оболочки promptvars
, которая по умолчанию), эта строка расширяется через расширение параметра, подстановку команд, арифметическое расширение и удаление цитат.
Тогда трюк должен иметь массив, который будет иметь значение k-го поля (до пустой строки) всякий раз, когда выполняется (k-1) -я команда. Затем, используя соответствующие расширения параметров, мы сможем определить, когда эти поля установлены, и отобразить код возврата предыдущей команды, если поле не установлено. Если вы хотите вызвать этот массив __cmdnbary
, просто выполните:
PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '
Облик:
$ PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '
0 $ [ 2 = 3 ]
1 $
$ # it seems that it works
$ echo "it works"
it works
0 $
Чтобы претендовать на самую короткую задачу ответа:
PS1='\n${a[\#]-$? }${a[\#]=}$ '
что 31 символ.
Не используйте это, конечно, поскольку a
является слишком тривиальным именем; также \$
может быть лучше, чем $
.
Кажется, вам не нравится, что начальное приглашение 0 $
; вы можете легко изменить это путем инициализации массива __cmdnbary
соответствующим образом: вы поместите это где-то в свой файл конфигурации:
__cmdnbary=( '' '' ) # Initialize the field 1!
PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '
Ответ 2
У нас есть время, чтобы поиграть в эти выходные. Глядя на мой более ранний ответ (не очень хороший) и другие ответы, я думаю, что это, возможно, самый маленький ответ.
Поместите эти строки в конец ~/.bash_profile
:
PS1='$_ret$ '
trapDbg() {
local c="$BASH_COMMAND"
[[ "$c" != "pc" ]] && export _cmd="$c"
}
pc() {
local r=$?
trap "" DEBUG
[[ -n "$_cmd" ]] && _ret="$r " || _ret=""
export _ret
export _cmd=
trap 'trapDbg' DEBUG
}
export PROMPT_COMMAND=pc
trap 'trapDbg' DEBUG
Затем откройте новый терминал и обратите внимание на это желаемое поведение в приглашении BASH:
$ uname
Darwin
0 $
$
$
$ date
Sun Dec 14 05:59:03 EST 2014
0 $
$
$ [ 1 = 2 ]
1 $
$
$ ls 123
ls: cannot access 123: No such file or directory
2 $
$
Объяснение:
- Это основано на крюках
trap 'handler' DEBUG
и PROMPT_COMMAND
.
-
PS1
использует переменную _ret
i.e. PS1='$_ret$ '
.
Команда -
trap
запускается только при выполнении команды, но PROMPT_COMMAND
запускается даже при нажатии пустого ввода.
Команда -
trap
устанавливает переменную _cmd
в фактически выполненную команду, используя BASH internal var BASH_COMMAND
.
-
PROMPT_COMMAND
устанавливает метки _ret
в "$? "
, если _cmd
не является пустым, в противном случае устанавливает _ret
в ""
. Наконец, он сбрасывает _cmd
var в пустое состояние.
Ответ 3
Переменная HISTCMD
обновляется каждый раз, когда выполняется новая команда. К сожалению, значение маскируется во время выполнения PROMPT_COMMAND
(я полагаю, по причинам, связанным с тем, что история не перепуталась с вещами, которые происходят в команде приглашения). Обходной путь, который я придумал, довольно грязный, но, похоже, он работает в моем ограниченном тестировании.
# This only works if the prompt has a prefix
# which is displayed before the status code field.
# Fortunately, in this case, there is one.
# Maybe use a no-op prefix in the worst case (!)
PS1_base=$'\n'
# Functions for PROMPT_COMMAND
PS1_update_HISTCMD () {
# If HISTCONTROL contains "ignoredups" or "ignoreboth", this breaks.
# We should not change it programmatically
# (think principle of least astonishment etc)
# but we can always gripe.
case :$HISTCONTROL: in
*:ignoredups:* | *:ignoreboth:* )
echo "PS1_update_HISTCMD(): HISTCONTROL contains 'ignoredups' or 'ignoreboth'" >&2
echo "PS1_update_HISTCMD(): Warning: Please remove this setting." >&2 ;;
esac
# PS1_HISTCMD needs to contain the old value of PS1_HISTCMD2 (a copy of HISTCMD)
PS1_HISTCMD=${PS1_HISTCMD2:-$PS1_HISTCMD}
# PS1_HISTCMD2 needs to be unset for the next prompt to trigger properly
unset PS1_HISTCMD2
}
PROMPT_COMMAND=PS1_update_HISTCMD
# Finally, the actual prompt:
PS1='${PS1_base#foo${PS1_HISTCMD2:=${HISTCMD%$PS1_HISTCMD}}}${_:+${PS1_HISTCMD2:+$? }}$ '
Логика в приглашении выглядит примерно так:
${PS1_base#foo...}
Отображает префикс. Материал в #...
полезен только для его побочных эффектов. Мы хотим сделать некоторые манипуляции с переменными без отображения значений переменных, поэтому мы спрячем их в подстановке строк. (Это покажет нечетные и, возможно, впечатляющие вещи, если значение PS1_base
когда-либо произойдет, начнется с foo
, за которым следует текущий индекс истории команд.)
${PS1_HISTCMD2:=...}
Это присваивает значение PS1_HISTCMD2
(если оно не установлено, что мы убедились в этом). Подстановка номинально также расширилась бы до нового значения, но мы спрятали ее в ${var#subst}
, как описано выше.
${HISTCMD%$PS1_HISTCMD}
Мы назначаем либо значение HISTCMD
(когда создается новая запись в истории команд, т.е. мы выполняем новую команду), либо пустую строку (когда команда пуста) до PS1_HISTCMD2
. Это работает путем обрезания значения HISTCMD
любого соответствия на PS1_HISTCMD
(с использованием синтаксиса замены суффикса ${var%subst}
).
${_:+...}
Это вопрос. Он будет расширяться до... что-то, если значение $_
установлено и непусто (что происходит при выполнении команды, но не в том случае, если мы выполняем назначение переменной). "Something" должен быть кодом состояния (и пробелом, для удобочитаемости), если PS1_HISTCMD2
непусто.
${PS1_HISTCMD2:+$? }
Там.
'$ '
Это только фактический суффикс предложения, как в исходном вопросе.
Таким образом, ключевыми частями являются переменные PS1_HISTCMD
, которые запоминают предыдущее значение HISTCMD
и переменную PS1_HISTCMD2
, которая фиксирует значение HISTCMD
, поэтому к нему можно получить доступ из PROMPT_COMMAND
, но необходимо чтобы быть отключенным в PROMPT_COMMAND
, чтобы присваивание ${PS1_HISTCMD2:=...}
снова срабатывало при следующем отображении приглашения.
Я немного поигрался с попыткой скрыть вывод из ${PS1_HISTCMD2:=...}
, но потом понял, что на самом деле есть что-то, что мы хотим отобразить, так что просто контрейлерные. Вы не можете иметь полностью пустой PS1_base
, потому что оболочка, видимо, замечает и даже не пытается выполнить подстановку, когда нет значения; но, может быть, вы можете придумать фиктивное значение (возможно, нет?), если у вас нет ничего другого, что вы хотите отобразить. Или, может быть, это может быть реорганизовано для работы с суффиксом; но это, вероятно, будет еще сложнее.
В ответ на вызов "наименьшего ответа" Анубхавы вот код без комментариев или проверки ошибок.
PS1_base=$'\n'
PS1_update_HISTCMD () { PS1_HISTCMD=${PS1_HISTCMD2:-$PS1_HISTCMD}; unset PS1_HISTCMD2; }
PROMPT_COMMAND=PS1_update_HISTCMD
PS1='${PS1_base#foo${PS1_HISTCMD2:=${HISTCMD%$PS1_HISTCMD}}}${_:+${PS1_HISTCMD2:+$? }}$ '
Ответ 4
Это, вероятно, не лучший способ сделать это, но, похоже, работает
function pc {
foo=$_
fc -l > /tmp/new
if cmp -s /tmp/{new,old} || test -z "$foo"
then
PS1='\n$ '
else
PS1='\n$? $ '
fi
cp /tmp/{new,old}
}
PROMPT_COMMAND=pc
Результат
$ [ 2 = 2 ]
0 $ [ 2 = 3 ]
1 $
$
Ответ 5
Мне нужно использовать большой script bash -preexec.sh.
Хотя мне не нравятся внешние зависимости, это единственное, что помогло мне избежать 1
в $?
после простого нажатия enter
без выполнения каких-либо команд.
Это относится к вашему ~/.bashrc
:
__prompt_command() {
local exit="$?"
PS1='\[email protected]\h: \w \$ '
[ -n "$LASTCMD" -a "$exit" != "0" ] && PS1='['${red}$exit$clear"] $PS1"
}
PROMPT_COMMAND=__prompt_command
[-f ~/.bash-preexec.sh ] && . ~/.bash-preexec.sh
preexec() { LASTCMD="$1"; }