我想编写一个PROMPT_COMMAND
响应之前提供给命令提示符的任何内容的代码。例如,要在广泛、信息丰富的提示或简单、紧凑的提示之间切换,如下所示:
mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $
仅当值不为空时,值才会添加到 shell 的历史记录中,因此我不能简单地测试最近的历史记录条目是否为空。set | grep some_command
运行后运行some_command
没有给我任何结果,因此似乎不存在包含该信息的环境变量。
我主要使用bash
,但对 POSIX 兼容的解决方案和其他 shell 感到好奇。
答案1
PROMPT_COMMAND
我最终根本不需要。感谢克里斯托弗为我指明了正确的方向。
相反,考虑这个文件ps1.prompt
:
${__cmdnbary[\#]+$(
echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$
然后我可以将其输入到我的PS1
:
PS1=$(cat ps1.prompt)
(你不必这样做,但我发现它对于插图和编辑来说很方便。)
所以我们看到:
mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@zoe: ~ $
我们正在使用数组 hack在这里展示bash
,但我们不使用的参数替换,而是${parameter:-word}
使用${parameter+word}
so 我们仅在没有先前的命令运行时触发。
这需要一些解释,因为我们被迫在逻辑中使用双重否定。
如何${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
运作
在最初的数组破解演示中,${__cmdnbary[\#]-word}${__cmdnbary[\#]=}
使用了该构造。 (为了清楚起见,我已替换$?
为word
)。如果您对参数扩展和数组不是特别熟悉(我不是),那么根本不清楚发生了什么。
首先,了解一下\#
每手册:
\#
该命令的命令编号
...
命令编号是当前 shell 会话期间执行的命令序列中的位置。
这意味着\#
只会改变当且仅当执行命令。如果用户在提示符下输入空行,则不会执行任何命令,因此\#
不会更改。
${__cmdnbary[#]=}中空字符串的设置
${__cmdnbary[\#]=}
使用参数扩展。回到手册:
${parameter:=word}
指定默认值。如果参数未设置或为空,则将单词的扩展分配给参数。然后替换参数的值。
因此,如果__cmdnbary[\#]
未设置或为 null,则此构造将分配一个空字符串(word
在我们的例子中是一个空字符串),并且整个构造将在我们的输出中替换为相同的空字符串。
__cmdnbary[\#]
将要总是当我们第一次看到它时,它会被取消设置或为 null,因为 # 是单调的——它总是递增或保持不变。 (也就是说,直到它循环,可能大约 2^31 或 2^63,但我们还会遇到其他问题长的在我们到达那里之前。我将这个解决方案描述为有点黑客行为是有原因的。)
条件式在${__cmdnbary[\#]-word}
${__cmdnbary[\#]-word}
是另一个参数扩展。从手册中:
${parameter:-word}
使用默认值。如果参数未设置或为空,则替换单词的扩展。否则,将替换参数的值。
所以,如果数组条目\#
是未设置或为空,word
在其位置使用。由于我们在检查之前不会尝试分配__cmdnbary[\#]
(使用替换${parameter:=word}
),因此第一次检查给定值时\#
应该会发现数组中的该位置未设置。
bash
使用稀疏数组
对于那些习惯 C 风格数组的人需要澄清一点。bash
实际上使用稀疏数组;直到你分配某物到数组中的某个位置,该位置未设置。空字符串与“null 或 unset”不同。
为什么我们使用 ${__cmdnbary[#]+word}${__cmdnbary[#]=} 代替
${__cmdnbary[\#]+word}${__cmdnbary[\#]=}
和 ${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use
${parameter:+word} instead of
${parameter:-word}`。
请记住${parameter:-word}
,只有当为 null 或未设置word
时,才会出现- 在我们的例子中,只有当我们parameter
没有设置数组中的位置,当且仅当\#
增加时我们不会这样做,这只有在我们刚刚执行命令时才会发生。
这意味着,对于${parameter:-word}
,我们只会呈现,word
如果我们没有执行了一个命令,这与我们想要做的恰恰相反。所以,我们用${parameter:-word}
它来代替。再次,来自手册:
${parameter:+word}
使用替代值。如果参数为空或未设置,则不会替换任何内容,否则将替换单词的扩展。
这是(不幸的是)更需要理解的双重否定逻辑,但你就是这样。
提示本身
我们已经解释了切换机制,但是提示本身又如何呢?
在这里,我用来$( ... )
包含提示的实质内容。主要是为了我自己的可读性;你不必那样做。您可以替换$( ... )
为通常在变量赋值中填充的任何内容。
为什么它是黑客攻击?
还记得我们如何向稀疏数组添加条目吗?我们不会删除这些条目,因此数组将永远增长,直到退出 shell 会话为止;外壳漏了PS1
。据我所知,没有办法未设置提示中的变量或数组位置。你可以尝试一下$()
,但你会发现它不起作用;对子 shell 内的变量命名空间所做的更改不会应用于派生子 shell 的空间。
你可能尝试在结果文件中使用mktmp
Early in your .bashrc
、 before assignment 和 stuff 信息;PS1
然后您可以将当前的内容\#
与存储在其中的内容进行比较,但现在您的提示取决于磁盘 I/O,这是在紧急情况下将自己锁定的好方法。