Подтвердить что ты не робот

Обнаружение пустой команды

Рассмотрим этот PS1

PS1='\n${_:+$? }$ '

Вот результат нескольких команд

$ [ 2 = 2 ]

0 $ [ 2 = 3 ]

1 $

1 $

Первая строка не показывает статус, как ожидалось, а следующие две строки показывают правильный код выхода. Однако в строке 3 был нажат только Enter, поэтому мне бы хотелось, чтобы статус уйти, как строка 1. Как я могу это сделать?

4b9b3361

Ответ 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"; }