bashrc中[-n“ $ PS1”]的目的


10

什么用途的[ -n "$PS1" ][ -n "$PS1" ] && source ~/.bash_profile;服务?这行包含在.bashrcdotfiles 回购中

Answers:


20

这是在检查外壳是否是交互式的。在这种情况下,仅~/.bash_profile在外壳是交互式的情况下才采购文件。

请参阅“此Shell是否具有交互性?” 在bash手册中,引用了该特定用法。(它还建议通过测试$-特殊变量是否包含i字符来检查外壳是否是交互式的,这是解决此问题的更好方法。)


如果shell是交互式的,至少bash会取消设置PS1和PS2。您可以使用看到自己亲手看到的内容( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ),它只是打印出来[]。这似乎zsh中并不尽相同,至少从实验......在任何情况下,意图[ -n "$PS1" ]是检查是否外壳是交互与否。
filbranden

3
bash当非交互式(您之前的注释中的键入)是IMO错误时,这会取消PS1的设置,而PS1并不是bash特定的变量,因此无需进行任何设置。它是唯一执行此操作的外壳程序(尽管即使是非交互式的,它yash也会设置PS1为默认值)。
斯特凡Chazelas

1
由于相关代码位于特定于bash的文件中,因此这似乎是一个合理的答案。其他答案解决了POSIX规范或其他shell的更一般的情况。您已经回答“这样做的目的是什么?” 在问题中保留意图,同时适当地搁置其余部分。很高兴知道bash在做什么以及可能还有更好的方法来实现目标。
杰夫·谢勒

如果它建议使用更可靠的替代方法(例如),我会认为它更完整[[ $- = *i* ]] && source ~/.bash_profile
查尔斯·达菲

@CharlesDuffy坦白说,我认为并没有什么问题[ -n "${PS1}" ],但是我仍然更新了答案,以强调bash手册还建议/建议检查$-以确定shell是否是交互式的,希望您能改善答案。干杯!
filbranden

19

这是做什么的

这是测试外壳是否是交互式的一种广泛的方法。注意,它只能在bash中工作,而不能与其他shell一起工作。因此,对于来说还可以(如果很愚蠢).bashrc,但不能在.profile其中使用(由sh读取,bash只是sh可能的实现之一,而不是最常见的实现)。

为何起作用(仅适用于bash!)

交互式外壳将shell变量PS1设置为默认提示字符串。因此,如果外壳是交互式的,PS1则进行设置(除非用户.bashrc已将其删除,但在顶部尚未发生这种情况.bashrc,您仍然可以认为这是一件很愚蠢的事情)。

相反,在bash中是正确的:bash的非交互式实例PS1在启动时未设置。请注意,此行为特定于bash,并且可以说是一个bug(为什么bash -c '… do stuff with $var…'varis 时不起作用PS1)。但是所有bash版本(包括4.4以下)(我写的是最新版本)都可以做到这一点。

许多系统都输出PS1到环境中。这是一个坏主意,因为有许多不同的shell使用,PS1但语法不同(例如bash的提示转义zsh的提示转义完全不同)。但是它已经足够广泛了,以至于在实践中,看到它PS1已经设置并不能可靠地表明Shell是交互式的。该外壳可能是PS1从环境继承的。

为什么在这里(误用)

.bashrc是bash在交互时在启动时读取的文件。一个不太为人所知的事实是,bash还会读取.bashrc登录外壳程序,并且bash的启发式方法得出的结论是这是一个远程会话(bash检查其父对象是rshd还是sshd)。在第二种情况下,不太可能PS1在环境中进行设置,因为尚未运行任何点文件。

但是,代码使用此信息的方式适得其反。

  • 如果该外壳是交互式外壳,则.bash_profile它将在该外壳中运行。但是.bash_profile是登录时脚本。它可能会运行某些旨在每个会话仅运行一次的程序。它可能会覆盖用户在运行该Shell之前故意将其设置为其他值的一些环境变量。.bash_profile在非登录外壳中运行会造成破坏。
  • 如果该外壳程序是非交互式远程登录外壳程序,则不会加载.bash_profile。但是在这种情况下,加载.bash_profile可能会很有用,因为非交互式登录外壳不会自动加载/etc/profile~/.profile

我认为人们这样做的原因是因为用户通过GUI登录(这是很常见的情况),并且将环境变量设置置于.bash_profile而不是中.profile。大多数GUI登录机制都调用.profile但不调用.bash_profile(读取.bash_profile需要在会话启动过程中运行bash,而不是sh)。使用此配置,当用户打开终端时,他们将获得其环境变量。但是,用户不会在GUI应用程序中获得其环境变量,这是造成混乱的非常常见的原因。解决方案是使用.profile而不是.bash_profile设置环境变量。在两者之间架起一座桥梁.bashrc.bash_profile带来的问题比解决的问题多。

该怎么做

有一种简单,可移植的方法来测试当前外壳是否是交互式的:测试是否-i启用了该选项。

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

这是非常有用的.bashrc,以读取.profile如果shell非交互式只 -即的代码做的正好相反!阅读.profile,如果是bash(非交互式)登录shell,如果它是一个交互的shell不读它。

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi

4
可能是值得注意的是,以测试一种更好的方式,如果shell是交互的是[[ -o interactive ]](KSH时,bash,zsh中)或case $- in (*i*) ...; esac(POSIX)
斯特凡Chazelas

2
PS1如果不以交互方式运行,我的bash(版本4.4.12)实际上似乎未设置。测试起来很容易:PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'不会打印任何内容,而会PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'打印$PS1bash启动文件中的set 值(它不会打印字符串“ cuckoo”)。
FooF

1
@StéphaneChazelas:POSIX不需要$-包含i带有交互式shell的内容。
schily

1
Bosh自2012年起开始这样做,以与ksh兼容。直到您的更改生效后,POSIX才要求这样做。
schily

1
坦率地说,我要[ -n "${PS1}" ] 说错是有点过头了,毕竟只有当有人导出PS1时它才会中断(在您的回答中,您说这是个坏主意,甚至进入了原因),但这不会影响无论如何bash(因为如果外壳是非交互的,因为它会设置PS1和PS2,所以它会设置为PS1和PS2。)也许使用诸如“粘性”之类的词或谈论该方法的“局限性”会更好。我不认为这完全是“错误的”。如果有任何问题导出PS1,那是肯定的!无论如何,感谢您对此进行详细介绍。
filbranden

1

似乎这个奇怪的概念是由于bash不是以POSIX shell克隆而是以Bourne Shell克隆开始的事实造成的。

结果,POSIX交互行为($ENV被称为交互式shell)已在以后添加到bash并且并不广为人知。

有一个外壳可以授予类似的行为。这是csh$prompt具有特定值的csh授予:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

但这既不适用于Bourne Shell也不适用于POSIX Shell。

对于POSIX Shell,唯一被授予的方法是将交互式Shell的代码放入文件中:

$ENV

具有外壳程序特定名称。这是例如

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

其他人提到了shell标志-i,但这对于可靠的编程不可用。POSIX既不需要它set -i,也不需要$-包含i用于交互式shell的。POSIX仅要求sh -i将shell强制为交互模式。

由于$PS1可以从环境中导入变量,因此即使在非交互模式下也可以具有值。bash unsets PS1在任何非交互式外壳中的事实不是由标准授予的,也不由任何其他外壳完成。

所以干净的编程(甚至使用bash)都是将交互式shell的命令放入$HOME/.bashrc


0

我首先要讲的是Debian,在大多数情况下,Ubuntu也会使用bash。后者涉及其他系统。

在shell启动文件的设置中,有很多意见。
我也有自己的看法,但是我将尝试显示正确设置的现有示例。
我将使用debuan,因为它很容易找到其文件示例。
而且debian被大量使用,因此设置已经过充分测试,

检查是否已设置PS1的目的是什么?

只找出外壳是否是交互式的。

debian和ubuntu中的默认值/etc/profile(来自/ usr / share / base-files / profile):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

if的内容为:if交互(PS1默认设置),并且它是一个bash shell(但不用作默认值sh),然后将PS1更改为特定的新PS1(而不是默认值)。

debian中默认值/etc/bash.bashrc还包含:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

这样做的作用很明显:如果没有交互(其余)。

但是,在下面/etc/skel/.bashrc是一个测试交互式shell的正确方法的示例(使用$-):

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

这应该清楚地说明PS1的原因和一种替代方法。

正确的顺序

应避免报告您的设置。
的顺序(从系统设置,以(对于bash)更具体的用户设置)是/etc/profile/etc/bash.bashrc~/.profile和最后~/.bashrc/etc/profile它将最广泛的效果(对于更多的shell)放在(由root拥有)中,然后是/etc/bash.bashrc(也由root拥有),但仅影响bash。然后是中的个人设置$HOME,第一个是~/.profile针对大多数shell和~/.bashrc(几乎等同于~/.bash_profile),仅适用于bash。

因此,错误的源头~/.bashrc~/.profile,它正在改变着一个特定的用户对于bash设置为一个较笼统的影响更多的炮弹。除非以这种方式完成

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

它检查bash是否正在运行,并且只有.bashrc在这种情况下才加载。

这是Debian做出的上游决定。基本原理在这里解释

事实上,相反的,采购~/.profile~/.bash_profile(或~/.bashrc)只重应用应该已经已经加载到一个特定的使用情况,因此一般规则“并不坏”(我不是说“好”)。我并不是说很好,因为这可能会导致文件源循环。就像子目录加载父目录时一样,这就是目录循环。

在这种交叉采购中,对交互式外壳的检查才有意义。仅当外壳是交互式的时才~/.bashrc加载,但反过来可能会加载~/.profile(或反之),在这种情况下,可以使用检查交互式外壳。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.