检测空命令

22 bash shell interactive ps1

考虑这个PS1

PS1='\n${_:+$? }$ '
Run Code Online (Sandbox Code Playgroud)

这是一些命令的结果

$ [ 2 = 2 ]

0 $ [ 2 = 3 ]

1 $

1 $
Run Code Online (Sandbox Code Playgroud)

第一行显示没有预期的状态,接下来的两行显示正确的退出代码.但是在第3行只有Enter被按下,所以我希望状态消失,就像第1行一样.我怎么能这样做?

gni*_*urf 17

这是一个有趣,非常简单的可能性:它使用\#转义序列PS1和参数扩展(以及Bash扩展其提示的方式).

转义序列\#扩展为要执行的命令的命令编号.每次实际执行命令时,这都会增加.试试吧:

$ PS1='\# $ '
2 $ echo hello
hello
3 $ # this is a comment
3 $
3 $    echo hello
hello
4 $
Run Code Online (Sandbox Code Playgroud)

现在,每次显示提示时,Bash首先展开找到的转义序列PS1,然后(如果设置了shell选项promptvars,这是默认值),则通过参数扩展,命令替换,算术扩展和报价删除.

然后诀窍是有一个数组,只要执行(k -1)-th命令,就会将第k个字段设置(到空字符串).然后,使用适当的参数扩展,我们将能够在这些字段设置为检测如果电场未设置显示前一个命令的返回码.如果要调用此数组,只需执行以下操作:__cmdnbary

PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '
Run Code Online (Sandbox Code Playgroud)

看:

$ PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '

0 $ [ 2 = 3 ]

1 $ 

$ # it seems that it works

$     echo "it works"
it works

0 $
Run Code Online (Sandbox Code Playgroud)

有资格获得最短的答案挑战:

PS1='\n${a[\#]-$? }${a[\#]=}$ '
Run Code Online (Sandbox Code Playgroud)

这是31个字符.

当然,不要使用这个,因为这a是一个太琐碎的名字; 也\$可能比$.


似乎你不喜欢最初的提示0 $; 您可以通过__cmdnbary适当地初始化数组来轻松地修改它:您将把它放在配置文件中的某个位置:

__cmdnbary=( '' '' ) # Initialize the field 1!
PS1='\n${__cmdnbary[\#]-$? }${__cmdnbary[\#]=}\$ '
Run Code Online (Sandbox Code Playgroud)


anu*_*ava 7

这周末有时间玩游戏.看看我之前的答案(不好)和其他答案,我认为这可能是最小的答案.

将这些行放在您的末尾~/.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
Run Code Online (Sandbox Code Playgroud)

然后打开一个新终端并在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 $
$
Run Code Online (Sandbox Code Playgroud)

说明:

  • 这是基于trap 'handler' DEBUGPROMPT_COMMAND钩子.
  • PS1正在使用变量_retie PS1='$_ret$ '.
  • trap命令仅在执行命令时运行,但PROMPT_COMMAND即使按下空输入也会运行.
  • trapcommand _cmd使用BASH internal var 将变量设置为实际执行的命令BASH_COMMAND.
  • PROMPT_COMMANDhook设置_ret"$? "if _cmd非为空,否则设置_ret"".最后它将_cmdvar 重置为空状态.


tri*_*eee 5

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:+$? }}$ '
Run Code Online (Sandbox Code Playgroud)

提示中的逻辑大致如下:

${PS1_base#foo...}
Run Code Online (Sandbox Code Playgroud)

这将显示前缀。里面的东西#...只对它的副作用有用。我们想要在不显示变量值的情况下进行一些变量操作,因此我们将它们隐藏在字符串替换中。(如果PS1_baseever的值刚好以开头,foo后跟当前命令历史记录索引,这将显示奇怪的和可能壮观的东西。)

${PS1_HISTCMD2:=...}
Run Code Online (Sandbox Code Playgroud)

这会为其分配一个值PS1_HISTCMD2(如果它未设置,我们已经确定它是)。替换在名义上也会扩展到新值,但我们已将其隐藏在 a 中${var#subst},如上所述。

${HISTCMD%$PS1_HISTCMD}
Run Code Online (Sandbox Code Playgroud)

我们将 的值HISTCMD(在命令历史记录中创建新条目时,即我们正在执行新命令时)或空字符串(当命令为空时)分配给PS1_HISTCMD2。这通过修剪HISTCMD任何匹配的值来工作PS1_HISTCMD(使用${var%subst}后缀替换语法)。

${_:+...}
Run Code Online (Sandbox Code Playgroud)

这是从问题。如果 的值$_已设置且非空(这是在执行命令时,但不是例如,如果我们正在执行变量赋值),它将扩展为...。如果PS1_HISTCMD2非空,“某物”应该是状态代码(为了易读性,还有一个空格)。

${PS1_HISTCMD2:+$? }
Run Code Online (Sandbox Code Playgroud)

那里。

'$ '
Run Code Online (Sandbox Code Playgroud)

这只是实际的提示后缀,就像在原始问题中一样。

所以关键部分是PS1_HISTCMD记住 的先前值HISTCMD的变量,以及PS1_HISTCMD2捕获 的值的变量,HISTCMD以便可以从 内部访问PROMPT_COMMAND,但需要在 中取消设置,PROMPT_COMMAND以便${PS1_HISTCMD2:=...}下次提示时再次触发赋值显示。

我试图隐藏输出,${PS1_HISTCMD2:=...}但后来意识到实际上我们想要显示一些东西,所以只是捎带它。你不能有一个完全空的,PS1_base因为 shell 显然注意到了,并且在没有值时甚至不会尝试执行替换;但也许你可以想出一个虚拟值(一个无操作的转义序列,也许?)如果你没有其他想要显示的东西。或者也许这可以重构为使用后缀运行;但这可能会更加棘手。

为了回应 Anubhava 的“最小答案”挑战,这里是没有注释或错误检查的代码。

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:+$? }}$ '
Run Code Online (Sandbox Code Playgroud)