3.1 Shell的语法
当shell读取输入时,它会进行一连串的操作。如果输入的内容是注释的开始,shell会忽略注释符号(‘#’),以及该行的其余部分。
否则,大致上说,shell会读取它的输入,并将输入分为单词和运算符,采用引号规则来选择赋予各种单词和字符的含义。
然后,shell将这些标记解析为命令和其他结构,删除某些单词或字符的特殊含义,扩展其他字符,根据需要重定向输入和输出,执行指定的命令,等待命令的退出状态,并使该退出状态可用于进一步的检查或处理。
1 Shell操作
以下是对shell读取和执行命令时的操作的简要描述。基本上,shell会做以下工作。
- 从文件(见3.8 Shell脚本)、作为-c调用选项的参数提供的字符串(见6.1 调用Bash)或从用户终端读取其输入。
- 将输入分成单词和运算符,遵守2 引号中描述的引号规则。这些标记被
metacharacters
分开。别名扩展由这一步执行(见6.6 别名)。 - 将标记解析为简单的和复合的命令(见3.2 Shell指令)。
- 执行各种shell扩展(见3.5 Shell的扩展),将扩展后的标记分解为文件名列表(见8 文件名扩展)以及命令和参数列表。
- 执行任何必要的重定向(见3.6 重定向),并从参数列表中删除重定向操作符和它们的操作数。
- 执行命令(见3.7 执行命令)。
- 可以选择等待命令完成,并收集其退出状态(见5 退出状态)。
2 引号
引号是用来消除某些字符或词对shell的特殊意义。引号可以用来取消对特殊字符的特殊处理,防止保留字被识别为特殊字符,并防止参数扩展。
每一个shell元字符(见2 定义)对shell来说都有特殊的意义,如果要表示它自己,必须加引号。 当使用命令历史扩展工具时(见9.3 历史扩展),历史扩展字符,通常是‘!’,必须加引号,以防止历史扩展。参见9.1 Bash 历史工具,了解有关历史扩展的更多细节。
有三种引号机制:转义字符、单引号和双引号。
2.1 转义字符
一个不加引号的反斜杠‘\’是Bash的转义字符。 它保留了接下来的字符的字面价值,但换行符
除外。如果出现了\newline
对,而反斜杠本身没有引号,那么\newline
将被视为续行(也就是说,它被从输入流中删除,并被有效忽略)。
2.2 单一引号
用单引号(‘'’)括起来的字符可以保留引号内每个字符的字面值。单引号不能出现在单引号之间,即使前面有反斜杠。
2.3 双引号
将字符括在双引号 (‘"’)中会保留引号内所有字符的字面值,但
‘$’, ‘`’, ‘\’,
以及启用历史扩展时的'!'除外。
当shell处于
POSIX 模式(参见 6.11 Bash的POSIX模式),
‘!’在双引号内没有特殊含义,即使启用了历史扩展。
字符‘$’和‘`’
在双引号内保留其的特殊含义(参见 3.5 Shell的扩展)。
仅当反斜杠后跟以下字符之一时,反斜杠才保留其特殊含义:
‘$’、‘`’、‘”’、‘\’或newline
。在双引号内,后面跟着这些字符之一的反斜杠将被删除。没有特殊含义的字符前面的反斜杠保持不变。双引号可以在双引号内引用,方法是在其前面加上一个反斜杠。如果启用,将执行历史扩展,除非“!”使用反斜杠转义出现在双引号中。‘!’前面的反斜杠没有被删除。
特殊参数‘*’和‘@’有特殊含义 当在双引号中时(参见 3 Shell参数扩展)。
2.4 ANSI-C引用
$'string' 形式的字符序列被视为特殊的一种单引号。序列扩展为 字符串,字符串中的反斜杠转义字符按照ANSI C标准的规定被替换。反斜杠转义序列(如果存在)按如下方式解码:
\a
警报(铃)
\b
退格键
\e
\E
转义字符(非ANSI C)。
\f
换页
\n
换行
\r
回车
\t
水平制表符
\v
垂直制表符
\\
反斜杠
\'
单引号
\"
双引号
\?
问号号
\nnn
八位字符,其值为八进制值nnn(一至三个八进制数字)。
\xHH
八位字符,其值为十六进制值HH(一个或两个十六进制数字)。
\uHHHH
统一码(ISO/IEC 10646)字符,其值为十六进制值HHH(一至四个十六进制数字)。
\UHHHHHHHH
统一码(ISO/IEC 10646)字符,其值为十六进制值HHHHH(一至八个十六进制数字)。
\cx
一个Control-x 字符
扩展后的结果是单引号,就像美元符号不存在一样。
2.5 本地特定的翻译
在双引号字符串前面加上一个美元符号(‘$’),例如$"hello, world",将导致根据当前语言环境翻译字符串。 gettext
基础结构使用LC_MESSAGES
、TEXTDOMAINDIR
和TEXTDOMAIN
shell 变量执行查找和翻译,如下所述。 有关此处未涉及的其他细节,请参阅gettext文档。如果当前的locale是C
或POSIX
,如果没有可用的翻译,或者如果字符串没有被翻译,美元符号将被忽略。 因为这是一种双引号的形式,无论是否被翻译和替换,默认情况下字符串仍然是双引号。 如果使用shopt
内置程序启用了noexpand_translation
选项(见2 Shopt 内置程序),翻译的字符串是单引号而不是双引号。
本节的其余部分简要介绍了如何使用 gettext 为名为 scriptname 的 shell 脚本中的字符串创建翻译。 在 gettext 文档中,有更多细节。
创建国际化的脚本
一旦你在脚本中用$"..."标记了你要翻译的字符串,你就可以使用命令创建一个gettext"模板"文件。
bash --dump-po-strings scriptname > domain.pot
domain是你的message domain。 它只是一个任意的字符串,用来识别 gettext 需要的文件,就像一个包或脚本的名字。它在安装翻译的系统上的所有消息域中必须是唯一的,所以 gettext 知道哪些翻译对应于你的脚本。 你将使用模板文件为每个目标语言创建翻译。 模板文件的后缀通常是 ‘.pot’ 。
你将此模板文件复制到您要支持的每种目标语言的单独文件中(称为“PO”文件,使用后缀“.po”)。PO 文件使用各种命名约定,但是当你将模板文件翻译成特定的文件时,您首先将模板文件复制到一个文件,该文件的名称是您要定位的语言,后缀为“.po”。例如,您的字符串的西班牙语翻译将在名为“es.po”的文件中,要开始使用名为“example”的消息域,您可以运行
cp example.pot es.po
最终,PO文件通常被命名为domain.po,并安装在包含某一特定语言的多个翻译文件的目录中。
无论你选择哪种命名方式,你都需要将PO文件中的字符串翻译成适当的语言。 这必须手动完成。
当你完成了翻译和PO文件后,你将使用gettext工具生成所谓的"MO"文件,这是gettext工具用来有效查找翻译的PO文件的编译版本。 MO文件也被称为"消息目录"文件。 你使用msgfmt
程序来做这件事。 例如,如果你有一个包含西班牙翻译的文件,你可以运行
msgfmt -o es.mo es.po
以产生相应的MO文件。
一旦你有了MO文件,你就决定把它们安装在哪里,并使用TEXTDOMAINDIR
shell变量来告诉gettext工具它们在哪里。 确保在安装MO文件时,使用与PO文件相同的消息域来命名它们。
你的用户将使用LANG
或LC_MESSAGES
shell变量来选择所需的语言。
你将TEXTDOMAIN
变量设置为脚本的消息域。 如上所述,你用消息域来命名你的翻译文件。
你,或者可能是你的用户,将TEXTDOMAINDIR
变量设置为存储消息目录文件的目录名称。 如果你将消息文件安装到系统的标准消息目录目录中,你就不需要担心这个变量。
消息目录文件的存储目录因系统而异。有些使用由LC_MESSAGES
变量选择的消息目录。其他人根据TEXTDOMAIN
变量的值创建消息目录的名称,可能会添加“.mo”后缀。如果您使用TEXTDOMAIN
变量,您可能需要将TEXTDOMAINDIR
变量设置为消息目录文件的位置,如上所述。通常以这种方式使用这两个变量:$TEXTDOMAINDIR
/$LC_MESSAGES
/LC_MESSAGES/$TEXTDOMAIN
.mo。
如果你使用了最后一个约定,并且你想把带有西班牙语(es)和世界语(eo)翻译的消息目录文件存储到你用来保存自定义翻译文件的本地目录中,你可以运行
TEXTDOMAIN=example TEXTDOMAINDIR=/usr/local/share/locale cp es.mo ${TEXTDOMAINDIR}/es/LC_MESSAGES/${TEXTDOMAIN}.mo cp eo.mo ${TEXTDOMAINDIR}/eo/LC_MESSAGES/${TEXTDOMAIN}.mo
当所有这些都完成后,包含编译后的翻译的消息目录文件被安装在正确的位置,你的用户将能够通过在运行你的脚本前设置LANG
或LC_MESSAGES
环境变量,看到任何被支持的语言的翻译字符串。
3 注释
在一个非交互式的shell中,或者在一个启用了shopt
内置的interactive_comments
选项的交互式shell中(见2 Shopt 内置程序),一个以‘#’开头的单词会导致该单词和该行的所有剩余字符被忽略。没有启用interactive_comments
选项的交互式shell不允许注释。在交互式shell中,interactive_comments
选项是默认打开的。 参见6.3 交互式shell,以了解什么使shell具有交互性。