Bash 你需要知道的 15 个特殊字符

若想深入掌握 Linux、macOS 或其他类 UNIX 系统中的 Bash shell,理解特殊字符(如~、*、| 和 >)至关重要。本文旨在帮助你揭开这些 Linux 命令的神秘面纱,让你在命令行操作中如鱼得水。

何谓特殊字符?

Bash shell 中,有一类字符被赋予双重含义。当你直接在 shell 中输入这些字符时,它们会被视为指令或命令,指示 shell 执行特定操作。你可以把它们看作是单字符的命令。

然而,有时候,你仅仅需要字符本身,而不是其特殊功能。此时,你需要一种方法来让这些字符以字面意义而非特殊含义被使用。

本文将详细介绍哪些字符属于“特殊”或“元”字符,并演示如何在功能和字面上使用它们。

~:用户主目录

波浪号 (~) 代表用户的主目录的快捷方式。这意味着你无需在命令中键入完整的用户主目录路径。无论你当前位于文件系统的哪个位置,你都可以通过以下命令快速回到你的主目录:

cd ~

此命令还可用于相对路径。例如,假设你当前不在主文件夹内,且想要进入工作目录下的 archive 文件夹,你可以使用波浪号:

cd ~/work/archive

.:当前目录

一个句点 (.) 表示当前目录。在 `ls` 命令中使用 `-a`(全部)选项时,你会在目录列表中看到它。

ls -a

你还可以在命令中使用句点来指定当前目录的路径。例如,若想从当前目录运行一个脚本,可以这样调用它:

./script.sh

这会告诉 Bash 在当前目录查找 `script.sh` 文件。通过这种方式,Bash 不会在路径中的其他目录中搜索匹配的可执行文件或脚本。

..:父目录

两个句点或“双点” (..) 表示当前目录的父目录。你可以使用它在目录树中向上移动一级。

cd ..

此命令也可以用于相对路径。例如,你可以先上移一级,然后再进入该级别的另一个目录。

你还可以使用此技术快速跳转到目录树中与当前目录同级的另一个目录。你先上移一级,然后再下移到另一个目录。

cd ../gc_help

/:路径目录分隔符

正斜杠 (/),通常被称为斜线,用于分隔路径名中的目录。

ls ~/work/archive

单个正斜杠表示最短的目录路径。由于 Linux 目录树中的一切都从根目录开始,你可以使用以下命令快速切换到根目录:

cd /

#:注释或字符串修剪

大多数情况下,井号或数字符号 (#) 用于告知 shell 其后的内容为注释,应被忽略。你可以在 shell 脚本中使用它,或者在命令行上使用它(尽管在命令行上用处不大)。

# This will be ignored by the Bash shell

但实际上,它并未被完全忽略,而是被添加到了你的命令历史记录中。

你还可以使用井号来修剪字符串变量,删除字符串开头的某些文本。下面的命令创建了一个名为 `this_string` 的字符串变量。

在此示例中,我们将文本 “Dave Geek!” 赋值给该变量。

this_string="Dave Geek!"

下面的命令使用 `echo` 在终端窗口中打印 “How-To”。它通过参数扩展检索字符串变量中存储的值。因为我们附加了 `#` 和文本 “Dave”,字符串中这一部分在传递给 `echo` 之前会被删除。

echo How-To ${this_string#Dave}

这不会改变字符串变量中存储的值;它只会影响发送给 `echo` 的内容。我们可以再次使用 `echo` 打印字符串变量的值进行验证:

echo $this_string

?:单字符通配符

Bash shell 支持三个通配符,问号 (?) 是其中之一。你可以使用通配符来替换文件名模板中的字符。包含通配符的文件名可以匹配一系列文件名,而不仅是一个文件名。

问号通配符恰好代表一个字符。考虑以下文件名模板:

ls badge?.txt

这可以理解为“列出所有以 ‘badge’ 开头,并且在文件扩展名之前有一个任意字符的文件”。

它将匹配以下文件。请注意,有些文件名的 “badge” 部分后面跟着数字,有些则跟着字母。问号通配符将匹配字母和数字。

但是,此文件名模板与 “badge.txt” 不匹配,因为文件名在 “badge” 和文件扩展名之间没有单个字符。问号通配符必须与文件名中的相应字符匹配。

你还可以使用问号查找文件名中具有特定字符数的所有文件。例如,以下命令列出文件名中恰好有五个字符的所有文本文件:

ls ?????.txt

*:字符序列通配符

星号 (`*`) 通配符可以代表任意字符序列,包括零个字符。考虑以下文件名模板:

ls badge*

它匹配 “badge.txt”,因为通配符代表任意字符序列或没有字符。

ls source.*

此命令会匹配所有名为 “source” 的文件,无论文件扩展名是什么。

[]:字符集通配符

正如上面提到的,问号代表任意单个字符,星号代表任意字符序列(包括零个字符)。你可以使用方括号 ([]) 以及它们所包含的字符来定义一个字符集。文件名中的相关字符必须与通配符字符集中的至少一个字符匹配。

ls badge_0[246].txt

在此示例中,该命令翻译为:“列出所有以 ‘badge_0’ 开头,紧随其后的字符为 2、4 或 6,且扩展名为 ‘.txt’ 的文件。”

ls badge_[01][789].txt

每个文件名模板可以使用多组方括号:

ls badge_[23][1-5].txt

你还可以在字符集中包含范围。以下命令选择文件名中编号为 21 到 25 以及 31 到 35 的文件。

;:Shell 命令分隔符

你可以在命令行上输入任意数量的命令,只需用分号 (;) 分隔每个命令即可。以下是一个示例:

ls > count.txt; wc -l count.txt; rm count.txt

请注意,即使第一个命令失败,第二个命令也会运行,即使第二个命令失败,第三个命令也会运行,以此类推。

cd ./doesntexist && cp ~/Documents/reports/* .

如果你希望当一个命令失败时停止执行序列,请使用双与号 (`&&`) 而不是分号。

&:后台进程

在终端窗口中输入命令并完成后,你将返回到命令提示符。通常,这只需一两秒钟。但是,如果你启动另一个应用程序(例如 `gedit`),在你关闭该应用程序之前,你将无法使用终端窗口。

gedit command_address.page &

但是,你可以将应用程序作为后台进程启动,并继续使用终端窗口。为此,只需在命令行末尾添加一个 `&` 符号即可。

Bash 会显示启动的进程 ID,然后将你返回到命令行。这样,你就可以继续使用终端窗口。

<:输入重定向

许多 Linux 命令接受文件作为参数,并从文件中获取数据。这些命令中的大多数也可以从数据流中获取输入。要创建数据流,请使用左尖括号 (`<`)。

sort < words.txt

当命令的输入被重定向时,它的行为可能与读取指定文件时有所不同。

如果我们使用 `wc` 计数文件中的单词、行和字符,它会打印这些值以及文件名。如果我们重定向文件的内容到 `wc`,它会打印相同的数值,但它不知道数据来源的文件名,因此不会打印文件名。

以下是一些如何使用 `wc` 的示例:

wc words.txt
wc < words.txt

>:输出重定向

你可以使用右尖括号 (`>`) 将命令的输出重定向到其他位置(通常是文件)。以下是一个示例:

ls > files.txt
cat files.txt

输出重定向也可以重定向错误信息。只需在 `>` 中添加数字(例如,在本例中为 2):

wc doesntexist.txt 2> errors.txt
cat errors.txt

|:管道

“管道”将命令连接在一起。它从一个命令获取输出,并将其作为输入提供给下一个命令。管道命令的数量(链的长度)是任意的。

在这里,我们使用 `cat` 将 `words.txt` 文件的内容作为输入传递给 `grep`,`grep` 会提取任何包含小写或大写“C”的行。然后,`grep` 将这些行传递给 `sort`。 `sort` 使用 `-r`(反向)选项,因此排序结果将以相反的顺序显示。

cat words.txt | grep [cC] | sort -r

!:逻辑非和历史运算符

感叹号 (!) 是表示逻辑非的运算符。

[ ! -d ./backup ] && mkdir ./backup

此命令行包含两个命令:

第一个命令是方括号内的文本;

第二个命令是双 `&&` 之后的文本。

第一个命令使用 `!` 作为逻辑运算符。方括号表示将要进行测试。 `-d`(目录)选项测试名为 `backup` 的目录是否存在。第二个命令创建目录。

因为双 `&&` 分隔了两个命令,所以如果第一个命令成功,Bash 只会执行第二个命令。然而,这与我们需要的相反。如果 “backup” 目录的测试成功,我们不需要创建它。如果 “backup” 目录的测试失败,那么第二条命令不会执行,也不会创建缺失的目录。

这就是逻辑运算符 `!` 发挥作用的地方。它充当逻辑非。因此,如果测试成功(即目录存在),`!` 将其翻转为“不成功”,即失败。所以,第二个命令没有被激活。

如果目录测试失败(即目录不存在),`!` 将响应更改为“不失败”,即成功。因此,执行创建缺失目录的命令。

小小的 `!` 可以在你需要的时候发挥巨大作用!

ls -l -d backup

要检查 `backup` 文件夹的状态,可以使用 `ls` 命令以及 `-l`(长列表)和 `-d`(目录)选项,如下所示:

!24

你还可以使用感叹号从命令历史记录中运行命令。 `history` 命令列出你的命令历史记录,然后你可以输入要重新运行的命令的编号加上感叹号执行它,如下所示:

!!

重新运行之前的命令:

$:变量表达式

在 Bash shell 中,你可以创建变量来保存值。有些变量,比如环境变量,总是存在,你可以随时打开终端窗口访问它们。它们保存着诸如用户名、主目录和路径等值。

echo $USER
echo $HOME
echo $PATH

你可以使用 `echo` 查看变量保存的值,只需在变量名称前加上美元符号 ($) 即可,如上所示。

ThisDistro=Ubuntu
MyNumber=2001
echo $ThisDistro
echo $MyNumber

要创建一个变量,你必须给它一个名称并为其赋值。创建变量时不需要使用美元符号。仅在引用变量时添加 `$`,例如在上面的示例中。

在美元符号周围添加大括号 ({}) 并执行参数扩展可以获取变量的值,并允许进一步转换该值。

MyString=123456qwerty

这将创建一个包含字符串的变量,如下所示:

echo ${MyString}

使用以下命令将字符串回显到终端窗口:

echo ${myString:6}

要返回从整个字符串的位置 6 开始的子字符串,请使用以下命令(存在零偏移,因此第一个位置为零):

echo ${myString:0:6}

如果要回显从位置 0 开始并包含接下来六个字符的子字符串,请使用以下命令:

echo ${myString:4:4}

使用以下命令回显从位置 4 开始并包含接下来的四个字符的子字符串:

引用特殊字符

如果要将特殊字符用作字面值(非特殊)字符,你必须告诉 Bash shell。这称为引用,有三种方法可以做到这一点。

如果将文本括在双引号 (“…”) 中,这将阻止 Bash 对大多数特殊字符进行操作,它们只会按原样打印。但是,一个值得注意的例外是美元符号 ($)。它仍然用作变量表达式的字符,因此你可以在输出中包含变量的值。

echo "Today is $(date)"

例如,此命令打印日期和时间:

echo 'Today is $(date)'

如果你将文本用单引号 (‘…’) 括起来,如下所示,它将停止所有特殊字符的功能:

echo "Today is $(date)"

你可以使用反斜杠 (\) 来防止其后的字符充当特殊字符。这被称为“转义”字符;请参见下面的示例:

只需将特殊字符视为非常短的命令。如果你记住它们的用法,那么你对 Bash shell 以及他人编写的脚本的理解会大有裨益。