深入了解定制 Bash
Bash 点文件
![](/data/attachment/album/202007/19/110018bl392bj3g3g41a23.jpg)


操作系统的最外层 —— 也就是跟你直接打交道的 —— 叫做 [shell](https://en.wikipedia.org/wiki/Shell_(computing))（“外壳”）。Fedora 预装了好几种不同的 shell。shell 可以是图形界面，或者字符界面。在文档中，你常常能见到两个母缩写词 GUI （<ruby> 图形用户界面 <rt>  Graphical User Interface </rt></ruby>）跟 CLI（<ruby> 命令行界面 <rt>  Command-Line Interface </rt></ruby>），它们用来区分图形和基于字符的 shell/界面。[GNOME](https://en.wikipedia.org/wiki/GNOME) 和 [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) 分别是 Fedora 默认的图形和命令行界面，不过你也可以使用其它 [GUI](https://fedoramagazine.org/fedoras-gaggle-of-desktops/) 或者 [CLI](https://en.wikipedia.org/wiki/Comparison_of_command_shells) shell。


接下来在这篇文章中，我们会讨论一些比较推荐的 Bash 字符界面的点文件配置。


### Bash 概览


Bash 参考手册中写道：



> 
> 根本上来说，shell 只是一个能执行命令的宏处理器。宏处理器这个词意味着通过扩展文本跟符号，从而构成更复杂的表达式。
> 
> 
> **Bash 参考手册** **第五版，Bash 5.0** **2019 年 5 月**
> 
> 
> 


除了能使用其它程序之外，Bash shell 还含有一些内置命令和关键字。Bash 内置功能很强大，甚至能够作为一门 [高级语言](https://en.wikipedia.org/wiki/High-level_programming_language) 独当一面。Bash 的几个关键字和操作符类似于 [C 语言](https://en.wikipedia.org/wiki/C_(programming_language))。


Bash 能够以交互式或非交互式模式启动。Bash 的交互模式是一个很多人都熟悉的典型的终端/命令行界面。[GNOME 终端](https://en.wikipedia.org/wiki/GNOME_Terminal) 默认以交互模式打开 Bash。Bash 在非交互模式下运行的例子是，当命令和数据从文件或 shell 脚本通过 [管道](https://en.wikipedia.org/wiki/Pipeline_(Unix)) 传送到 Bash 时。其它 Bash 可以运行的模式包括：<ruby> 登录 <rt>  login </rt></ruby>、<ruby> 非登录 <rt>  non-login </rt></ruby>、<ruby> 远程 <rt>  remote </rt></ruby>、POSIX、Unix sh、<ruby> 受限 <rt>  restricted </rt></ruby>，以及使用与用户不同的 UID/GID 模式。各种模式是可以相互组合的。比如，交互式 + 受限 + POSIX 或者非交互式 + 非登录 + 远程。不同的启动模式，决定着 Bash 所读取的启动文件。理解这些操作模式，有助于帮助我们修改启动文件。


根据 Bash 参考手册，它会：



> 
> 1. 从文件中...、从作为 `-c` 调用选项传入参数的字符...，或者从用户的终端中读取输入。
> 2. 将输入分解成单词和操作符，遵循 [它的] 引用规则。...这些标记使用元字符隔开。这一步执行别名展开。
> 3. 将标记解析成简单与复合命令。
> 4. 执行各种 shell 展开...，将展开之后的标记分解成文件名...以及命令和参数的列表。
> 5. 执行必要的重定向...并从参数列表中去除重定向操作符及其操作数。
> 6. 执行命令。
> 7. 必要时等待命令完成，并收集退出状态。
> 
> 
> **Bash 参考文档** **第五版，Bash 版本 5.0** **2019 年 5 月**
> 
> 
> 


当用户开启终端模拟器进入命令行环境时，便启动了一次交互式 shell 会话。GNOME 终端默认以非登录模式为用户打开 Shell。你可以在 “<ruby> 编辑 → 首选项 → 配置文件 → 命令 <rt>  Edit → Preferences → Profilles → Command </rt></ruby>” 中配置 GNOME 终端以何种模式（登录与非登录式）启动。也可以在 Bash 启动时通过向其传递 `-login` 标志来要求进入登录模式。要注意一点，Bash 的登录模式与非交互模式并不互斥。可以让 Bash 同时以登录模式和非交互模式运行。


### 启动 Bash


除非传入 `-noprofile` 选项，否则登录模式的 Bash shell 会默认读取并执行某些初始化文件中命令。如果 `/etc/profile` 存在，它会是第一个被执行的文件，紧接着是按 `~/.bash_profile`、`~/.bash_login` 或 `~/.profile`顺序找到的第一个文件。当用户退出登录模式的 shell 时，或者有脚本在非交互式登录模式的 shell 中调用了内置 `exit`命令，Bash 会读取并执行 `~/.bash_logout` 中的命令，如果 `/etc/bash_logout` 存在的话，会紧接着执行它。通常来说，`/etc/profile` 会<ruby> 援引 <rt>  source </rt></ruby> `/etc/bashrc` 文件，读取并执行其中的命令，然后查找并读取执行 `/etc/profile.d` 目录中以 `.sh` 结尾的文件。同样的，`~/.bash_profile` 通常也会<ruby> 援引 <rt>  source </rt></ruby> `~/.bashrc` 文件。`/etc/bashrc` 和 `~/.bashrc` 都会进行检查，避免重复<ruby> 援引 <rt>  source </rt></ruby>。


（LCTT 译注：在 Bash 中，脚本会通过 `source` 或 `.` 命令来将另外一个脚本引入其中，这个行为称之为 “source”、“sourcing”，但是该行为一直没有公认且常用的翻译方法。经过多番斟酌，我认为可以译做“援引”，理由如下：1、“援引”具有“引用、引入”的意思，符合该行为；2、“援”这个词的发音和“source” 常见的汉语意思“源”同音，便于记忆。以上是我们的愚见，供大家参考讨论。—— 老王，2020/7/19）


一个交互式的 shell，如果不是登录 shell，那么当它第一次被调用的时候，会执行 `~/.bashrc` 文件。这是用户在 Fedora 上打开终端时通常会进入的 shell 类型。当 Bash 以非交互模式启动 —— 就像运行脚本时那样 —— 它会寻找 `BASH_ENV`环境变量。如果找到了，就会展开它的值作为文件名，接着读取并执行该文件。效果跟执行以下命令相同：



```
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

```

值得注意的是，不会使用 `PATH` 环境变量的值来搜索该文件名（LCTT 译注：意即不会检索搜索路径）。


### 重要的用户点文件


Bash 最广为人知的用户点文件是 `~/.bashrc`。通过编辑该文件，可以设置大多数个性化选项。由于我们常常需要设置一些选项，会改动上面提及甚至没有提及的文件，所以大部分自定义选项会成为一个麻烦事。Bash 环境具有很高的可定制性，正是为了适应不同用户的不同需求。


![](/data/attachment/album/202007/19/110445g5z2rorc3ww353bo.png)


当登录 shell 正常退出时，如果 `~/.bash_logout` 和 `/etc/bash_logout` 存在，它们会被调用。下一幅图展示了 Bash 作为交互式 shell 启动时的过程。例如，当用户从桌面环境打开终端模拟器时，会按照以下顺序进行。


![](/data/attachment/album/202007/19/110446i6mynysngyinoz5y.png)


我们已经知道，在不同的启动模式下，Bash 会执行不同的命令，所以很明显，只有几种最需要关注的典型启动模式。分别是非交互、交互式登录 shell，和非交互式、交互式非登录 shell。如果想定义某些全局环境，那么需要将一个具有唯一名称、以 `.sh` 为后缀的文件（例如 `custom.sh`）放置在 `/etc/profile.d` 目录。


对于非交互式非登录启动方式，需要特别注意。在这种模式下，Bash 会检查 `BASH_ENV` 变量。如果定义了该变量，Bash 会援引它所指向的文件。另外，处理 `BASH_ENV` 时并不会使用 `PATH` 变量所存储的值（LCTT 译注：意即不会检索搜索路径），所以它必须包含执行文件的绝对路径。比如说，如果有人希望非交互式执行脚本时，shell 能读取 `~/.bashrc` 文件中的设置，那么他可以把类似下面这样的内容放在一个名为 `/etc/profile.d/custom.sh` 的文件中...



```
# custom.sh
.
.
.
# 如果使用 Fedora Workstation
BASH_ENV="/home/username/.bashrc"
.
.
.
# 如果使用 Fedora Silverblue Workstation
BASH_ENV="/var/home/username/.bashrc"

export BASH_ENV

```

上面这份脚本会让每个 shell 脚本在运行之前先执行该用户的 `~/.bashrc`。


用户一般都会自定义他们的系统环境，以便契合他们自己的工作习惯与偏好。举例来说，用户可以通过别名来实现这种程度的自定义。拥有相同起始参数、需要频繁使用的命令是制作别名的最佳选择。以下展示了一些来自 `~/.bashrc` 文件中定义的别名。



```
# .bashrc
# 执行全局文件
if [ -f /etc/bashrc ];
   then . /etc/bashrc
fi
.
.
.
# 用户别名和函数
alias ls='ls -hF --color=auto'
alias la='ls -ahF --color=auto'

# 让 dir 命令用起来像在 Windows 那样
alias dir='ls --color=auto --format=long'

# 用颜色高亮 grep 结果
alias grep='grep --color=auto'

```

在系统中，别名是一种自定义各种命令的方法。它能减少击键次数，而且让命令用起来更方便。针对用户级别的别名通常存放在该用户的 `~/.bashrc` 文件中。


如果你发现自己经常要去历史中查找曾经执行过的某条命令，那可能需要改改历史设置了。你依然可以在 `~/.bashrc` 文件中设置针对用户级别的历史选项。比如说，如习惯同时使用多个终端，那你可能要启用 `histappend` 选项。某些 Bash 相关的 shell 选项本质上是布尔值（接收 `on` 或 `off`），通常可以用内置命令 `shopt` 启用或禁用。接收更复杂的值的 Bash 选项（如 `HISTTIMEFORMAT`），常常通过赋值给环境变量来达到配置目的。以下演示如何以 shell 选项和环境变量定制 Bash。



```
# 配置 Bash 历史

# 用制表符扩展目录环境变量，并设置 histappend
shopt -s direxpand histappend

# ignoreboth 等同于 ignorespace 和 ignoredup
HISTCONTROL='ignoreboth'

# 控制 `history` 输出中的时间格式
HISTTIMEFORMAT="[%F %T] "

# 无限历史记录
# NB：在新版 Bash 中，任何 < 0 的写法都有效，但是在 CentOS/RHEL 中，只有这样才行得通
HISTSIZE=
HISTFILESIZE=

# 或者对于使用新版 Bash 的人
HISTSIZE=-1
HISTFILESIZE=-1

```

上面例子中的 `direxpand` 选项，可以让 Bash 在文件名补全时，用单词展开结果替换目录名。它会改变 readline 编辑缓冲区的内容，所以你所输入的东西已经被补全得到的结果替换了。


`HISTCONTROL` 变量用于启用或禁用命令历史的某些过滤选项。重复行、以空白打头的行，都能通过该选项将它们从命令历史中过滤掉。引用自 Dusty Mabe，这是我从他那儿得到的技巧：



> 
> `ignoredup` 可以让历史不记录重复条目（如果你反复执行同一条命令）。`ignorespace` 会忽略前面有空白的条目，当你在设置一个包含敏感信息的环境变量或者执行一条不想被记录进磁盘的命令时，这就很有用。`ignoreboth`相当于这两条选项的结合体。
> 
> 
> **Dusty Mabe – Redhat首席软件工程师，2020.6.19**
> 
> 
> 


对于命令行重度用户，Bash 有一个 `CDPATH` 环境变量。如果 `CDPATH` 包含一系列供 `cd` 命令搜索的目录，且提供一个相对路径作为第一个参数，那么它会按顺序检查所有列出的目录，寻找匹配的子目录并切换到第一个匹配结果目录。



```
# .bash_profile

# 设置 CDPATH
CDPATH="/var/home/username/favdir1:/var/home/username/favdir2:/var/home/username/favdir3"

# 也可以写成这样
CDPATH="/:~:/var:~/favdir1:~/favdir2:~/favdir3"

export CDPATH

```

`CDPATH` 通常像 `PATH` 一样的方式进行更新 —— 通过在赋值右侧引用自身来保留原来的值。



```
# .bash_profile

# 设置 CDPATH
CDPATH="/var/home/username/favdir1:/var/home/username/favdir2:/var/home/username/favdir3"

# 或者写成这样
CDPATH="/:~:/var:~/favdir1:~/favdir2:~/favdir3"

CDPATH="$CDPATH:~/favdir4:~/favdir5"

export CDPATH

```

`PATH` 是另一个极其重要的变量。它是系统上的命令的搜索路径。注意，有些应用要求将它们自己的目录加入 `PATH` 变量，这样才能正常使用。跟 `CDPATH` 一样，通过在赋值右侧引用原值来追加新值到 `PATH` 变量。如果你希望将新值前置，只需要把原来的值（`$PATH`）放到列表末尾即可。还有注意的是在 Fedora，这一列值通过冒号分隔（`:`）。



```
# .bash_profile

# 添加 PATH 值到 PAHT 环境变量
PATH="$PATH:~/bin:~:/usr/bin:/bin:~/jdk-13.0.2:~/apache-maven-3.6.3"

export PATH

```

命令提示符是另一个流行的自定义选项。它有七个可定制的参数：



> 
> * `PROMPT_COMMAND`：如果设置了，会在每一个主提示符（`$PS1`）出现之前执行该值。
> * `PROMPT_DIRTRIM`：如果设置成大于零的数，则该值用作展开 `\w` 和 `\W` 提示符字符串转义符时保留的尾随目录组件数量。删除的字符将替换为省略号。
> * `PS0`：这个参数的值像 `PS1` 一样展开，在交互式 shell 读取命令之后、执行命令之前展示。
> * `PS1`：主提示符字符串。默认值是 `\s-\v\$` 。
> * `PS2`：次提示符字符串。默认是 `>` 。在显示之前，`PS2` 像 `PS1` 那样展开。
> * `PS3`：这个参数的值用作 `select` 命令的提示符。如果这个变量没有设置，`select` 命令会用 `#?`  作为提示符。
> * `PS4`：这个参数的值像 `PS1` 那样展开，如果设置了 `-x` 选项，这个展开值会在命令行被回显之前作为提示符显示。展开后的值的第一个字符在必要时会复制数次，指示间接层数。默认值是 `+` 。
> 
> 
> **Bash 参考文档** **第五版，Bash 版本 5.0** **2019 年 5 月**
> 
> 
> 


Bash 的这一个方面就可以用整篇文章来讨论。你可以找到许许多多信息和例子。在本文末尾链接的存储库中提供了一些点文件范例，包括提示符重新配置。你可以随意使用该存储库中的例子进行学习和体验。


### 总结


既然你已经掌握了一些 Bash 的工作原理，那就可以轻松修改你的 Bash 点文件，满足你自己的需求和习惯。美化你的提示符，制作一些别名，这样你的电脑才真的属于你。查看 `/etc/profile`、`/etc/bashrc` 和 `/etc/profile.d/` 这些文件的内容，获得一些启发。


你也可以在这里写一些关于终端模拟器的评论。有很多办法可以将你最喜欢的终端，完全配置成你想要的样子。你或许早就想到了，但是通常可以通过……嗯……用户家目录的点文件实现这个目的。终端模拟器也可以作为登录会话启动，有些人总喜欢用登录环境。你使用终端和电脑的姿势，取决于你如何修改（或不修改）你的点文件。


如果你很好奇自己的命令行处于什么会话状态，使用下面这个脚本来判断一下。



```
#!/bin/bash

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

```

把这几行放到一个文件里，加上可执行权限，然后运行，就能看到你当前处于何种类型的 shell。`$-` 在 Bash 中是一个变量，如果是交互式 shell，它会包含字母 `i`。此外，你可以直接输出 `$-` 变量然后检查它的输出中是否含有 `i` 标记。



```
$ echo $-

```

### 参考信息


可以参考以下资料以获取更多信息和示例。Bash 手册也是一个很好的信息来源。请注意，确保你的本地手册页记录了你当前运行的 Bash 版本的特性，因为在网上找到的信息有时可能太老（过时了）或太新（你的系统还没有安装）。


* <https://opensource.com/tags/command-line>
* <https://opensource.com/downloads/bash-cheat-sheet>（在该网站中，你必须要输入一个有效的电子邮箱地址，或者注册，才能下载。）
* <https://opensource.com/article/19/12/bash-script-template>


对本文有各种形式（点文件示例、提示，以及脚本文件）贡献的社区成员：


* Micah Abbott – 首席质量工程师
* John Lebon – 首席软件工程师
* Dusty Mabe – 首席软件工程师
* Colin Walters – 高级首席软件工程师


示例点文件和脚本可以在这个存储库中找到：


* <https://github.com/TheOneandOnlyJakfrost/bash-article-repo>


请仔细检查上面所提供的存储库中的信息。有些可能已经过时了。里面还包含很多开发中的自定义脚本和<ruby> 宠物容器 <rt>  pet container </rt></ruby>配置例子，那些不是点文件。我推荐从 John Lebon 的点文件开始学习，从头到尾都含有完善的解说，它们是我见过的最详细的，并且包含了非常好的描述。祝你学得开心！




---


via: <https://fedoramagazine.org/customizing-bash/>


作者：[Stephen Snow](https://fedoramagazine.org/author/jakfrost/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[nophDog](https://github.com/nophDog) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
The outermost layer of your operating system – the part you interact with – is called the [shell](https://en.wikipedia.org/wiki/Shell_(computing)). Fedora comes with several preinstalled shells. Shells can be either graphical or text-based. In documentation, you will often see the acronyms GUI (Graphical User Interface) and CLI (Command-Line Interface) used to distinguish between graphical and text-based shells/interfaces. Other [GUI](https://fedoramagazine.org/fedoras-gaggle-of-desktops/) and [CLI](https://en.wikipedia.org/wiki/Comparison_of_command_shells) shells can be used, but [GNOME](https://en.wikipedia.org/wiki/GNOME) is Fedora’s default GUI and [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is its default CLI.

The remainder of this article will cover recommended dotfile practices for the Bash CLI.

## Bash overview

From the Bash reference manual:

At its base, a shell is simply a macro processor that executes commands. The term macro processor means functionality where text and symbols are expanded to create larger expressions.

Reference Documentation for Bash

Edition 5.0, for Bash Version 5.0.

May 2019

In addition to helping the user start and interact with other programs, the Bash shell also includes several built-in commands and keywords. Bash’s built-in functionality is extensive enough that it is considered a [high-level programming language](https://en.wikipedia.org/wiki/High-level_programming_language) in its own right. Several of Bash’s keywords and operators resemble those of [the C programming language](https://en.wikipedia.org/wiki/C_(programming_language)).

Bash can be invoked in either interactive or non-interactive mode. Bash’s interactive mode is the typical terminal/command-line interface that most people are familiar with. [GNOME Terminal](https://en.wikipedia.org/wiki/GNOME_Terminal), by default, launches Bash in interactive mode. An example of when Bash runs in non-interactive mode is when commands and data are [piped](https://en.wikipedia.org/wiki/Pipeline_(Unix)) to it from a file or shell script. Other modes of operation that Bash can operate in include: login, non-login, remote, POSIX, unix sh, restricted, and with a different UID/GID than the user. Various combinations of these modes are possible. For example interactive+restricted+POSIX or non-interactive+non-login+remote. Which startup files Bash will process depends on the combination of modes that are requested when it is invoked. Understanding these modes of operation is necessary when modifying the startup files.

According to the Bash reference manual, Bash …

1. Reads its input from a file …, from a string supplied as an argument to the -c invocation option …, or from the user’s terminal.

2. Breaks the input into words and operators, obeying [its] quoting rules. … These tokens are separated by metacharacters. Alias expansion is performed by this step.

3. Parses the tokens into simple and compound commands.

4. Performs the various shell expansions …, breaking the expanded tokens into lists of filenames … and commands and arguments.

5. Performs any necessary redirections … and removes the redirection operators and their operands from the argument list.

6. Executes the command.

7. Optionally waits for the command to complete and collects its exit status.

Reference Documentation for Bash

Edition 5.0, for Bash Version 5.0.

May 2019

When a user starts a terminal emulator to access the command line, an interactive shell session is started. GNOME Terminal, by default, launches the user’s shell in non-login mode. Whether GNOME Terminal launches the shell in login or non-login mode can be configured under *Edit* → *Preferences* → *Profiles* → *Command*. Login mode can also be requested by passing the *–login* flag to Bash on startup. Also note that Bash’s *login* and *non-interactive* modes are not exclusive. It is possible to run Bash in both *login* and *non-interactive* mode at the same time.

## Invoking Bash

Unless it is passed the *—**noprofile* flag, a Bash login shell will read and execute the commands found in certain initialization files. The first of those files is */etc/profile* if it exists, followed by one of *~/.bash_profile*, *~/.bash_login*, or *~/.profile*; searched in that order. When the user exits the login shell, or if the script calls the *exit* built-in in the case of a non-interactive login shell, Bash will read and execute the commands found in *~/.bash_logout* followed by */etc/bash_logout* if it exists. The file */etc/profile* will normally source */etc/bashrc*, reading and executing commands found there, then search through */etc/profile.d* for any files with an *sh* extension to read and execute. As well, the file *~/.bash_profile* will normally source the file *~/.bashrc*. Both */etc/bashrc* and *~/.bashrc* have checks to prevent double sourcing.

An interactive shell that is not a login shell, will source the *~/.bashrc* file when it is first invoked. This is the usual type of shell a user will enter when opening a terminal on Fedora. When Bash is started in non-interactive mode – as it is when running a shell script – it will look for the *BASH_ENV* variable in the environment. If it is found, will expand the value, and use the expanded value as the name of a file to read and execute. Bash behaves just as if the following command were executed:

if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

It is important to note that the value of the *PATH* variable is not used to search for the filename.

## Important user-specific dotfiles

Bash’s best-known user dotfile is *~/.bashrc*. Most user customization is done by editing this file. Most user customization, may be a stretch since there are reasons to modify all of the mentioned files; as well as other files that have not been mentioned. Bash’s environment is designed to be highly customizable in order to suit the needs of many different users with many different tastes.

![](https://fedoramagazine.org/wp-content/uploads/2020/06/bash-initialization-1-1024x711.png)

When a Bash login shell exits cleanly, *~/.bash_logout* and then */etc/bash_logout* will be called if they exist. The next diagram is a sequence diagram showing the process Bash follows when being invoked as an interactive shell. The below sequence is followed, for example, when the user opens a terminal emulator from their desktop environment.

![](https://fedoramagazine.org/wp-content/uploads/2020/06/bash-initialization-2-1024x544.png)

Armed with the knowledge of how Bash behaves under different invocation methods, it becomes apparent that there are only a few typical invocation methods to be most concerned with. These are the non-interactive and interactive login shell, and the non-interactive and interactive non-login shell. If global environment customizations are needed, then the desired settings should be placed in a uniquely-named file with a *.sh* extension (*custom.sh*, for example) and that file should be placed in the */etc/profile.d* directory.

The non-interactive, non-login invocation method needs special attention. This invocation method causes Bash to check the *BASH_ENV* variable. If this variable is defined, the file it references will be sourced. Note that the values stored in the *PATH* environment variable are not utilized when processing *BASH_ENV*. So it must contain the full path to the file to be sourced. For example, if someone wanted the settings from their *~/.bashrc* file to be available to shell scripts they run non-interactively, they could place something like the following in a file named */etc/profile.d/custom.sh* …

# custom.sh . . . #If Fedora Workstation BASH_ENV="/home/username/.bashrc" . . . #If Fedora Silverblue Workstation BASH_ENV="/var/home/username/.bashrc" export BASH_ENV

The above profile drop-in script will cause the user’s *~/.bashrc* file to be sourced just before every shell script is executed.

Users typically customizie their system environment so that it will better fit their work habits and preferences. An example of the sort of customization that a user can make is an alias. Commands frequently run with the same set of starting parameters are good candidates for aliases. Some example aliases are provided in the *~/.bashrc* file shown below.

# .bashrc # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi . . . # User specific aliases and functions alias ls='ls -hF --color=auto' alias la='ls -ahF --color=auto' # make the dir command work kinda like in windows (long format) alias dir='ls --color=auto --format=long' # make grep highlight results using color alias grep='grep --color=auto'

Aliases are a way to customize various commands on your system. They can make commands more convenient to use and reduce your keystrokes. Per-user aliases are often configured in the user’s *~/.bashrc* file.

If you find you are looking back through your command line history a lot, you may want to configure your history settings. Per-user history options can also be configured in *~/.bashrc*. For example, if you have a habit of using multiple terminals at once, you might want to enable the *histappend* option. Bash-specific shell options that are [boolean](https://en.wikipedia.org/wiki/Boolean_data_type) in nature (take either *on* or *off* as a value) are typically enabled or disabled using the *shopt* built-in command. Bash settings that take a more complex value (for example, *HISTTIMEFORMAT*) tend to be configured by assigning the value to an environment variable. Customizing Bash with both shell options and environment variable is demonstrated below.

# Configure Bash History # Expand dir env vars on tab and set histappend shopt -s direxpand histappend # - ignoreboth = ignorespace and ignoredup HISTCONTROL='ignoreboth' # Controls the format of the time in output of `history` HISTTIMEFORMAT="[%F %T] " # Infinite history # NB: on newer bash, anything < 0 is the supported way, but on CentOS/RHEL # at least, only this works HISTSIZE= HISTFILESIZE= # or for those of us on newer Bash HISTSIZE=-1 HISTFILESIZE=-1

The *direxpand* option shown in the example above will cause Bash to replace directory names with the results of word expansion when performing filename completion. This will change the contents of the readline editing buffer, so what you typed is masked by what the completion expands it to.

The *HISTCONTROL* variable is used to enable or disable some filtering options for the command history. Duplicate lines, lines with leading blank spaces, or both can be filtered from the command history by configuring this setting. To quote Dusty Mabe, the engineer I got the tip from:

Dusty Mabe – Redhat Principle Software Engineer, June 19, 2020

ignoredupmakes history not log duplicate entries (if you are running a command over and over).ignorespaceignores entries with a space in the front, which is useful if you are setting an environment variable with a secret or running a command with a secret that you don’t want logged to disk.ignorebothdoes both.

For users who do a lot of work on the command line, Bash has the *CDPATH* environment variable. If *CDPATH* is configured with a list of directories to search, the *cd* command, when provided a relative path as its first argument, will check all the listed directories in order for a matching subdirectory and change to the first one found.

# .bash_profile # set CDPATH CDPATH="/var/home/username/favdir1:/var/home/username/favdir2:/var/home/username/favdir3" # or could look like this CDPATH="/:~:/var:~/favdir1:~/favdir2:~/favdir3"

*CDPATH* should be updated the same way *PATH* is typically updated – by referencing itself on the right hand side of the assignment to preserve the previous values.

# .bash_profile # set CDPATH CDPATH="/var/home/username/favdir1:/var/home/username/favdir2:/var/home/username/favdir3" # or could look like this CDPATH="/:~:/var:~/favdir1:~/favdir2:~/favdir3" CDPATH="$CDPATH:~/favdir4:~/favdir5"

*PATH* is another very important variable. It is the search path for commands on the system. Be aware that some applications require that their own directories be included in the *PATH* variable to function properly. As with *CDPATH*, appending new values to *PATH* can be done by referencing the old values on the right hand side of the assignment. If you want to prepend the new values instead, simply place the old values (*$PATH*) at the end of the list. Note that on Fedora, the list values are separated with the colon character (**:**).

# .bash_profile # Add PATH values to the PATH Environment Variable PATH="$PATH:~/bin:~:/usr/bin:/bin:~/jdk-13.0.2:~/apache-maven-3.6.3" export PATH

The command prompt is another popular candidate for customization. The command prompt has seven customizable parameters:


PROMPT_COMMANDIf set, the value is executed as a command prior to issuing each primary prompt ($PS1).

PROMPT_DIRTRIMIf set to a number greater than zero, the value is used as the number of trailing directory components to retain when expanding the \w and \W prompt string escapes. Characters removed are replaced with an ellipsis.

PS0The value of this parameter is expanded likePS1and displayed by interactive shells after reading a command and before the command is executed.

PS1The primary prompt string. The default value is ‘\s-\v\$‘. …

PS2The secondary prompt string. The default is ‘‘.>PS2is expanded in the same way asPS1before being displayed.

PS3The value of this parameter is used as the prompt for theselectcommand. If this variable is not set, theselectcommand prompts with ‘#?‘Reference Documentation for Bash

PS4The value of this parameter is expanded likePS1and the expanded value is the prompt printed before the command line is echoed when the-xoption is set. The first character of the expanded value is replicated multiple times, as necessary, to indicate multiple levels of indirection. The default is ‘‘.+

Edition 5.0, for Bash Version 5.0.

May 2019

An entire article could be devoted to this one aspect of Bash. There are copious quantities of information and examples available. Some example dotfiles, including prompt reconfiguration, are provided in a repository linked at the end of this article. Feel free to use and experiment with the examples provided in the repository.

## Conclusion

Now that you are armed with a little knowledge about how Bash works, feel free to modify your Bash dotfiles to suit your own needs and preferences. Pretty up your prompt. Go nuts making aliases. Or otherwise make your computer truly yours. Examine the content of */etc/profile*, */etc/bashrc*, and */etc/profile.d/* for inspiration.

Some comments about terminal emulators are fitting here. There are ways to setup your favorite terminal to behave exactly as you want. You may have already realized this, but often this modification is done with a … wait for it … dotfile in the users home directory. The terminal emulator can also be started as a login session, and some people always use login sessions. How you use your terminal, and your computer, will have a bearing on how you modify (or not) your dotfiles.

If you’re curious about what type session you are in at the command line the following script can help you determine that.

#!/bin/bash case "$-" in (*i*) echo This shell is interactive ;; (*) echo This shell is not interactive ;; esac

Place the above in a file, mark it executable, and run it to see what type of shell you are in. *$-* is a variable in Bash that contains the letter **i** when the shell is interactive. Alternatively, you could just echo the $- variable and inspect the output for the presence of the **i** flag:

$ echo $-

## Reference information

The below references can be consulted for more information and examples. The Bash man page is also a great source of information. Note that your local man page is guaranteed to document the features of the version of Bash you are running whereas information found online can sometimes be either too old (outdated) or too new (not yet available on your system).

[https://opensource.com/tags/command-line](https://opensource.com/tags/command-line)

[https://opensource.com/downloads/bash-cheat-sheet](https://opensource.com/downloads/bash-cheat-sheet)

You will have to enter a valid email address at the above site, or sign up, to download from it.

[https://opensource.com/article/19/12/bash-script-template](https://opensource.com/article/19/12/bash-script-template)

Community members who provided contributions to this article in the form of example dotfiles, tips, and other script files:

- Micah Abbott – Principal Quality Engineer
- John Lebon – Principal Software Engineer
- Dusty Mabe – Principal Software Engineer
- Colin Walters – Senior Principal Software Engineer

A repository of example dotfiles and scripts can be found here:

[https://github.com/TheOneandOnlyJakfrost/bash-article-repo](https://github.com/TheOneandOnlyJakfrost/bash-article-repo)

Please carefully review the information provided in the above repository. Some of it may be outdated. There are many examples of not only dotfiles for Bash, but also custom scripts and pet container setups for development. I recommend starting with John Lebon’s dotfiles. They are some of the most detailed I have seen and contain very good descriptions throughout. Enjoy!

## Esc

Respect, wow

## Duke

Engineering precision. Nice job!

## Vernon Van Steenkist

A couple of my favorites.

set -o vi

This command puts your shell in vi keybindings mode (normally it is in Emacs keybindings mode). Especially useful if you use a keyboard that doesn’t have arrow keys like an IPhone or IPad. Good quick tutorial and references at

https://catonmat.net/bash-vi-editing-mode-cheat-sheet

Automatically add sub-directories to your PATH

export PATH=$PATH$(find $HOME/.scripts -not ( -name CVS -prune ) -type d -printf “:%p”)

This command adds all the sub-directories in $HOME/.scripts which contain my personal scripts to my PATH while excluding the CVS version control directories.

Also, if you quickly don’t want to run the alias version of a command, just put a backslash in front of it. For example

\ls

will output a directory listing without all the color etc directives that you put in you alias version of ls.

## geirha

Never export CDPATH! You’ll get some unpleasant surprises when a script using cd accidentally hits a path from CDPATH

## Stephen Snow

Hello @geirha,

Thank you for the information. Could you possibly elaborate on the “unpleasant surprises” for everyone’s edification?

Stephen

## geirha

Sorry, I meant to link to the pitfalls page. Here:

https://mywiki.wooledge.org/BashPitfalls#pf48

In short, if cd gets a hit in CDPATH, it will output the absolute path of the directory it ends up in (to alert the user it “ended up” elsewhere), which causes breakage if a script is using cd as part of a command substitution

## Stephen Snow

Hello,

Thanks for the link. I corrected the article to reflect your advice. I could see this being a problem under certain situations.

## geirha

You should also move it to .bashrc. .bash_profile is only meant to be read during login, so it’s mainly useful for setting environment variables like PATH, not for setting variables that configure your interactive session like HISTSIZE, PS1 and CDPATH.

## Jan

Hi Stephen,

geirha possibly refers to the following:

https://mywiki.wooledge.org/BashPitfalls#export_CDPATH.3D.:.2BAH4-.2FmyProject

## Stephen Snow

Thank you @Jan,

I think exporting CDPATH is fine since the potential error would really be a problem of the script writer as opposed to a problem with CDPATH being exported. The script writer should never assume anything about the environment of the system beforehand. When I write scripts for a specific function on my system, I know the environment and write with that in mind, but not so for generic scripts I am sharing with others. Therefore I then have to account for the potential pitfalls within the script or with specific instructions on how to use it.

## Steve

Additionally, always have “.” as the first component of your CDPATH variable, else you shall see surprising behaviour (eg; you have, ~/images and CDPATH=~/Projects/. Now, if you are in ~ and do ‘cd images’ you will end up in ~/Projects/images, if it exists)

## Robin Meade

I like how the author followed the best practice of putting PATH customizations in

, which is where Fedora always put them prior to this commit: https://src.fedoraproject.org/rpms/bash/c/739b272e5f5d10cf27a847a44d09eb7f4b6ec89b?branch=master

## Gregory Bartholomew

Putting PATH in the login profile makes more sense to me as well. Especially when you understand that environment variables are always inherited by sub-processes. It is inefficient and you shouldn’t need to recreate or redefine a bunch of variables every time you spawn a shell. Doing that is also quite error-prone as can be seen from commit https://src.fedoraproject.org/rpms/bash/c/e3b3cd9cec4a3bd12a792536c0ea131f5ba5bd72?branch=master.

I also don’t like that /etc/bashrc gets sourced from /etc/profile or that everything under /etc/profile.d gets sourced by both /etc/profile and /etc/bashrc. Everything sourcing everything else, multiple times, seems like a bit of an inefficient mess. And I’ve seen plenty of cases where scripts under /etc/profile.d ended up creating massively long environment variables as well because of this problem (it’s not just a problem for the PATH environment variable). And what if I place a script under /etc/profile.d that does something like a file system mount? Would I potentially end up with infinite mounts on top of each other? Would I have to wait on the mount to complete every time a bash shell gets spawned?

What if, for efficiency’s sake, I want to spawn a bash login shell with the just the minimal login environment defined, but I don’t want to run all the start-up scripts that every installed program has dropped under the /etc directory? From the documentation, it would seem like bash –norc ought to do that, but the way everything is sourced (multiple times) from everything else, it appears that that option to the bash command is completely meaningless at this time.

I think a much cleaner design would be to simply source ~/.bash_profile once on bash login shells (falling back to /etc/profile if that doesn’t exist). And to always source ~/.bashrc (falling back to /etc/bashrc if that doesn’t exist). ~/.bash_profile should be for stuff that should only be run once and ~/.bashrc should be stuff that should be run every time.

Just my two cents.

## Stephen Snow

Hello Gregory,

Multiple sourcing is a poor practice, and there is a check in /etc/bashrc to prevent dual sourcing it but not in ~/.bashrc. I chose to present the tips as the GNU Bash manual indicates. For example, the documentation purposely shows alias’s and functions in /etc/bashrc and ~/.bashrc and this is noted in comments in the profile files /etc/profile and ~/.bash_profile as well.

From /etc/profile …

Stephen

## Gregory Bartholomew

Yeah, those comments seem inaccurate at the least. If they were true, how would ~/.bash_profile and ~/.bashrc get called? I know they aren’t called from the global scripts.

That check for double sourcing appears unreliable/bogus too. What if I run “bash –login” followed by “bash –login”? Since the only double sourcing check in done in /etc/bashrc, and since everything under /etc/profile.d is sourced from /etc/profile, all the scripts under /etc/profile.d will still get double sourced.

I suspect there should really be a /etc/bashrc.d in addition to /etc/profile.d so that there would be a cleaner divide between the two types of scripts rather than all the stuff under /etc/profile.d getting run many many many times over every time a bash script is run somewhere.

Again, just my two cents. There may well be some details of which I am unaware that make a cleaner divide between run-once and run-always scripts impractical.

## Stephen Snow

The article was more about Bash than Fedora per se. I didn’t mean to give the impression that you should follow everything I typed there verbatim. In fact I would expect those interested to review the material, read the links to get more informed, then customize their system to their own needs. What I was trying to get across specifically was the order of startup, what files are or could be accessed then, and how might you do some customization to suit your workflow and use case needs.

## George N. White III

In these days of projects with participants spread across multiple organizations and using different linux distros it is worth mentioning a common source of confusion when fedora users interact with debian/Ubuntu or macOS users. It is helpful to realize that users of other distros may have dash (default system shell on debian) or zsh (default user shell on recent macOS).

## Stephen Snow

Hello, and thank you for noting there are different default shells in use by other Linux Distro’s as well as Mac’s. I had originally opened this article stating that there is a large variety of choice of shell to use even in Fedora, but the focus would be on Bash, and it’s use within the context of a Fedora system. The broader topic of different shells, and their relative benefits/drawbacks would be a rather large (amount of) content (for a magazine article) if you wanted to attempt to do the topic justice. It would likely need to be a multi-part series. Generally, as a preference, I use zsh on my Fedora system. For this article and some time leading up to it, I have been using Bash.

## John

Please correct me if I’m wrong but in Wayland the ‘profile’ scripts are no longer sourced.

## Stephen Snow

Hello John,

You are likely referring to the discussion around this https://ask.fedoraproject.org/t/ld-library-path-is-not-set-by-bash-profile-anymore-after-upgrade-to-fedora-31/4247/13. In particular, this is relevant in how the startup scripts are processed, and in fact if they are processed as expected in the first place. The article is written from the POV of the Bash manual, so there are certain expectations on the startup behavior and the subsequent behavior of the shell. Specifically, that the distro is following the suggested sequence of login and startup for processing the environment scripts. This is not so clear cut now with Fedora as of F31, since for instance the Gnome Session is now managed by systemd(https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/). So the ~/.bash_profile file is sourced (for now, but this may change) and for the interactive terminal sessions ~/.bashrc is still sourced.

## Gregory Bartholomew

For anyone who might be interested, I’ve found that an easy way to block a lot of the double sourcing is to add the following line to /etc/profile.d/sh.local:

The above will block /etc/bashrc from ever being run, however, so if you use it, you will need to duplicate anything in /etc/bashrc that you want in your ~/.bashrc. For me, this amounted to the following:

You want to keep ~/.bashrc very minimal because everything in it is run every time bash is executed (without the –norc parameter).

I’ve also tweaked my ~/.bash_profile and added a /etc/profile.d/ps1.sh to set the bash prompt for login shells. The end result of editing these four files is that now when I run just

bash(or when a bash script is run), there is no chance that any of the content under /etc/profile.d will be re-evaluated. This is easily verified because spawning a non-login bash shell reveils the default bash prompt (because /etc/profile.d/ps1.sh was not sourced). Below are the four files I’ve changed in their entirety. This is just an experiment at this time, so there may be problems with this setup that I have not noticed yet./etc/profile.d/sh.local

/etc/profile.d/ps1.sh

~/.bash_profile

~/.bashrc

I’ve also set gnome-terminal to always run bash as a login shell. This makes sense because the shell that gnome-terminal starts is never the child process of another bash shell — it cannot inherit an already initialized bash environment, so it needs to be run as login shell each time.

## Robin Meade

Cool! Thanks for writing that up.

I don’t find the need to set gnome-terminal to always run bash as a login shell. On my system, gnome-session is run by a bash login shell. My process table shows:

I do need to log out and back in for changes to my .bash_profile to take effect; that’s the only downside I see to not setting gnome-terminal to always run bash as a login shell.

## Gregory Bartholomew

Yeah, it looks like you have a somewhat non-standard setup there where gnome-session is a child process of a bash login shell.

I think your best option to get around having to logout and back in in that case would be to create something like ~/.gnome-terminal that ends with “exec bash”. Then set as a custom command for gnome-terminal something like:

/bin/bash -c “source $HOME/.gnome-terminal”

instead of your login shell. I haven’t tested such a setup, but it should give your a third option for where you can place configuration settings that would not require you to log out and back in (and also would not get run every time bash is run).

Just an idea,

gb

## Gregory Bartholomew

Just realized that you should probably check for the existence of the file first, so maybe something more like:

And if you want to eliminate the requirement that ~/.gnome-terminal end in “exec bash”, maybe something like:

## Robin Meade

OK, thanks Gregory. I was running Gnome using X. But with Wayland, bash is still executed as a login shell when gnome-session starts. See https://gitlab.gnome.org/GNOME/gnome-session/-/blob/master/gnome-session/gnome-session.in Anyway, I’m OK with needing to log out and back into my Gnome session to pick-up any changes to environment variables that I set in my ~/.bash_profile.

## Gregory Bartholomew

Interesting. I too now see that gnome-session is listed as being spawned from a bash login shell. However, experiments suggest that gnome-terminal is not a child of that bash login shell. I still have to set gnome-terminal to run bash as a login shell to get the content under /etc/profile.d processed (e.g. PS1) with my setup.

What I can make of the output of pstree seems to suggest that gnome-terminal is being spawned directly from systemd somehow (or perhaps from DBus as the article Steven linked earlier suggests).

## smeagol

export HISTTIMEFORMAT=”%m/%d/%y %H:%M:%S ”

^^ this is what I use. Do you like it? Can it be improved upon?