跳到内容跳到页面导航:上一页 [access key p]/下一页 [access key n]
documentation.suse.com / SUSE Linux Enterprise Server 文档 / 管理指南 / 常用任务 / Bash 和 Bash 脚本
适用范围 SUSE Linux Enterprise Server 15 SP3

1 Bash 和 Bash 脚本

现今,许多人都在使用装有 GNOME 之类图形用户界面 (GUI) 的计算机。尽管 GUI 可提供许多功能,但执行自动化任务时,这些功能会受到限制。外壳是对 GUI 的很好补充,本章将会概述外壳(在本例中为 Bash 外壳)的某些方面。

1.1 什么是外壳

通常来说,Linux 外壳就是 Bash (Bourne again Shell)。在本章中提到外壳时,指的是 Bash。除了 Bash 以外,还存在其他一些外壳(ash、csh、ksh、zsh 等),每种外壳都具备不同的功能和特征。如果需要关于其他外壳的更多信息,请在 YaST 中搜索外壳

1.1.1 Bash 配置文件

外壳可调用为:

  1. 交互式登录外壳: 当登录计算机时需要使用此方式,即使用 --login 选项调用 Bash 或通过 SSH 登录到远程计算机时。

  2. 普通交互式外壳: 通常在启动 xterm、konsole、gnome-terminal 或类似的命令行界面 (CLI) 工具时调用此外壳。

  3. 非交互式外壳: 当在命令行调用外壳脚本时调用此外壳。

根据所用外壳的类型,会读取不同的配置文件。下表显示登录和非登录外壳的配置文件。

表 1.1︰ 登录外壳的 Bash 配置文件

文件

说明

/etc/profile

不要修改此文件,否则在下一次更新时可能损坏您的修改!

/etc/profile.local

如果扩展 /etc/profile,请使用此文件

/etc/profile.d/

包含特定程序的系统范围配置文件

~/.配置文件

在此处插入特定于用户的登录外壳配置

请注意,登录外壳还会获取表 1.2 “非登录外壳的 Bash 配置文件”中所列的配置文件。

表 1.2︰ 非登录外壳的 Bash 配置文件

/etc/bash.bashrc

不要修改此文件,否则在下一次更新时可能损坏您的修改!

/etc/bash.bashrc.local

使用此文件插入系统范围的修改(仅 Bash)

~/.bashrc

在此处插入特定于用户的配置

此外,Bash 还使用更多文件:

表 1.3︰ 用于 Bash 的特殊文件

文件

说明

~/.bash_history

包含已键入的所有命令的列表

~/.bash_logout

注销时执行

~/.alias

用户为常用命令定义的别名。有关定义别名的更多细节,请参见 man 1 alias

非登录外壳

下面两个特殊外壳会阻止用户登录到系统:/bin/false/sbin/nologin。当用户尝试登录到系统时,这两个外壳都将失败且不会显示任何提示。这是针对系统用户有意设计的一种安全措施,虽然新式 Linux 操作系统提供了更有效的工具(例如 PAM 和 AppArmor)来控制系统访问。

SUSE Linux Enterprise Server 上的默认行为是将 /bin/bash 指派给人类用户,将 /bin/false/sbin/nologin 指派给系统用户。但由于历史遗留原因,nobody 用户拥有 /bin/bash,而该用户是系统用户默认使用的最低特权用户。但是,如果有多个系统用户使用 nobody,则使用 nobody 而获得的任何一点安全性都将失去。应该可以将它更改为 /sbin/nologin;最快的测试方法是进行此更改,然后看看是否中断了任何服务或应用程序。

使用以下命令列出 /etc/passwd 中已指派给所有用户(系统用户和人类用户)的外壳。输出视您系统上的服务和用户而异:

tux > sort -t: -k 7 /etc/passwd | awk -F: '{print $1"\t" $7}' | column -t
tux               /bin/bash
nobody            /bin/bash
root              /bin/bash
avahi             /bin/false
chrony            /bin/false
dhcpd             /bin/false
dnsmasq           /bin/false
ftpsecure         /bin/false
lightdm           /bin/false
mysql             /bin/false
postfix           /bin/false
rtkit             /bin/false
sshd              /bin/false
tftp              /bin/false
unbound           /bin/false
bin               /sbin/nologin
daemon            /sbin/nologin
ftp               /sbin/nologin
lp                /sbin/nologin
mail              /sbin/nologin
man               /sbin/nologin
nscd              /sbin/nologin
polkitd           /sbin/nologin
pulse             /sbin/nologin
qemu              /sbin/nologin
radvd             /sbin/nologin
rpc               /sbin/nologin
statd             /sbin/nologin
svn               /sbin/nologin
systemd-coredump  /sbin/nologin
systemd-network   /sbin/nologin
systemd-timesync  /sbin/nologin
usbmux            /sbin/nologin
vnc               /sbin/nologin
wwwrun            /sbin/nologin
messagebus        /usr/bin/false
scard             /usr/sbin/nologin

1.1.2 目录结构

下表简要介绍 Linux 系统上最重要的较高级别目录。以下列表中是关于这些目录和重要子目录的更多详细信息。

表 1.4︰ 标准目录树概述

目录

内容

/

根目录 — 目录树的起点。

/bin

基本二进制文件,例如系统管理员和普通用户都需要的命令。通常还包含外壳,如 Bash。

/boot

引导加载程序的静态文件。

/dev

访问特定于主机的设备所需的文件。

/etc

特定于主机的系统配置文件。

/home

储存系统上具有帐户的所有用户的用户主目录。但是,root 的主目录不在 /home 中,而是在 /root 中。

/lib

基本共享库和内核模块。

/media

可卸媒体的挂载点。

/mnt

临时挂载文件系统的挂载点。

/opt

附加应用程序软件包。

/root

超级用户 root 的主目录。

/sbin

基本系统二进制文件。

/srv

系统提供的服务的数据。

/tmp

临时文件。

/usr

具有只读数据的辅助层次结构。

/var

变量数据,如日志文件。

/windows

只在系统上同时安装了 Microsoft Windows* 和 Linux 时可用。包含 Windows 数据。

以下列表提供有关这些目录中有哪些文件和子目录的更多详细信息,并给出一些示例:

/bin

包含 root 和其他用户都可使用的基本 shell 命令。这些命令包括 lsmkdircpmvrmrmdir/bin 也包含 Bash,它是 SUSE Linux Enterprise Server 中的默认外壳。

/boot

包含引导所需的数据,如引导加载程序、内核以及内核开始执行用户模式程序之前使用的其他数据。

/dev

储存代表硬件组件的设备文件。

/etc

包含控制诸如 X Window 系统等程序操作的本地配置文件。/etc/init.d 子目录包含引导过程中可执行的 LSB init 脚本。

/home/USERNAME

储存在系统中建立帐户的所有用户的私人数据。这里的文件只能由其拥有者或系统管理员修改。默认情况下,电子邮件目录和个人桌面配置以隐藏文件和目录的形式储存在此处,例如 .gconf/.config

注意
注意:网络环境中的用户主目录

如果在网络环境中工作,则您的用户主目录可能映射到文件系统中除 /home 之外的其他目录中。

/lib

包含引导系统和运行 root 文件系统中的命令所需的基本共享库。共享库相当于 Windows 中的 DLL 文件。

/media

包含 CD-ROM、闪存盘和数码相机(如果它们使用 USB)等可卸媒体的挂载点。/media 通常包含除系统硬盘之外的各类驱动器。可卸媒体插入或连接到系统并挂载之后,您便可从此处访问该媒体。

/mnt

此目录提供临时挂载的文件系统的挂载点。root 可以在此处挂载文件系统。

/opt

保留用于安装第三方软件。在此处可以找到可选软件和较大附加产品程序包。

/root

root 用户的主目录。root 的个人数据储存在此处。

/run

systemd 和各个组件使用的 tmpfs 目录。/var/run/run 的符号链接。

/sbin

s 所表明的,该目录储存超级用户的实用程序。/sbin 包含 /bin 中的二进制文件以及引导和恢复系统所需的其他二进制文件。

/srv

储存系统提供的服务(如 FTP 和 HTTP)的数据。

/tmp

此目录由需要临时储存文件的程序使用。

重要
重要:在引导时清理 /tmp

系统重引导后,之前储存在 /tmp 中的数据无法保证仍然存在。这视情况而定,例如,取决于 /etc/tmpfiles.d/tmp.conf 中所做的设置。

/usr

/usr 与用户无关,而是 Unix 系统资源 (Unix system resource) 的缩写。/usr 中的数据是可以在符合文件系统层次标准 (FHS) 的各个主机之间共享的静态只读数据。此目录包含所有应用程序(包括 GNOME 等图形桌面),并且会在文件系统中创建次要层次。/usr 储存了多个子目录,例如 /usr/bin/usr/sbin/usr/local/usr/share/doc

/usr/bin

包含一般可访问的程序。

/usr/sbin

包含为系统管理员保留的程序,例如维修功能。

/usr/local

在此目录中,系统管理员可以安装本地的独立于分发包的扩展。

/usr/share/doc

储存系统的各种文档文件和发行描述。在 manual 子目录中可以找到此手册的联机版本。如果安装了多种语言,则此目录可能包含这些手册不同语言的版本。

packages 下可以找到系统上安装的软件包中包含的文档。对于每个包,都会创建一个子目录 /usr/share/doc/packages/PACKAGENAME,通常用其储存该包的自述文件,有时储存示例、配置文件或附加脚本。

如果系统上安装了操作指南,/usr/share/doc 还会包含 howto 子目录,其中有与 Linux 软件的安装和操作相关的许多任务的附加文档。

/var

/usr 用于储存静态只读的数据,而 /var 用于在系统操作期间写入并成为变量数据的数据,例如日志文件或假脱机数据。有关最重要日志文件的概述,可以在 /var/log/ 下找到,请参见见表 40.1 “日志文件”

1.2 编写外壳脚本

使用外壳脚本可以方便地完成各种任务:收集数据、在文本中搜索单词或短语,以及执行其他有用的操作。以下示例显示用于打印文本的小外壳脚本:

例 1.1︰ 用于打印文本的外壳脚本
#!/bin/sh 1
# Output the following line: 2
echo "Hello World" 3

1

第一行开头是 Shebang 字符 (#! ),它表示此文件是一个脚本。在 Shebang 后面指定的解释器将执行该脚本。在本例中,指定的解释器为 /bin/sh

2

第二行是一个以哈希符号开头的注释。我们建议对难以理解的行提供注释。提供适当的注释可以记住该行的用途和功能。另外,可帮助其他阅读者理解您的脚本。在开发社区中,注释被视为一种良好的做法。

3

第三行使用内置命令 echo 打印相应文本。

在运行此脚本之前,必须满足几个先决条件:

  1. 每个脚本都应包含 Shebang 行(如上面的示例所示)。如果该行缺失,您需要手动调用解释器。

  2. 可以将该脚本保存在任何位置。但是,建议将其保存在外壳可以找到的目录中。外壳中的搜索路径由环境变量 PATH 确定。通常,一般用户不具有对 /usr/bin 的写权限。因此,建议将脚本保存在用户目录 ~/bin/ 中。在上例中使用名称 hello.sh

  3. 该脚本需要可执行权限。使用以下命令设置权限:

    tux > chmod +x ~/bin/hello.sh

如果已满足上述所有先决条件,则可以按如下方式执行此脚本:

  1. 作为绝对路径: 可以使用绝对路径执行脚本。在本例中为 ~/bin/hello.sh

  2. 所有位置: 如果 PATH 环境变量包含脚本所在目录,则可以使用 hello.sh 来执行该脚本。

1.3 重定向命令事件

每个命令都可以使用三个通道输入或输出:

  • 标准输出: 这是默认的输出通道。在命令打印某些内容时都会使用标准输出通道。

  • 标准输入: 如果一个命令需要用户或其他命令输入,则使用此通道。

  • 标准错误: 命令使用此通道报告错误。

要重定向这些通道,有以下可行的操作方式:

命令 > 文件

将该命令的输出保存为文件,将删除现有文件。例如,ls 命令会将其输出写入文件 listing.txt

tux > ls > listing.txt
命令 >> 文件

将命令输出追加到文件。例如,ls 命令会将其输出追加到文件 listing.txt

tux > ls >> listing.txt
命令 < 文件

读取该文件作为给定命令的输入。例如,read 命令会将此文件的内容读入变量:

tux > read a < foo
命令 1 | 命令 2

将左侧命令的输出重定向为右侧命令的输入。例如,cat 命令会输出文件 /proc/cpuinfo 的内容。grep 使用此输出仅过滤出包含 cpu 的行:

tux > cat /proc/cpuinfo | grep cpu

每个通道都有一个文件描述符:0(零)表示标准输入,1 表示标准输出,2 表示标准错误。允许在 <> 字符前插入此文件描述符。例如,以下行搜索以 foo 开头的文件,但通过将错误重定向到 /dev/null 而抑制错误。

tux > find / -name "foo*" 2>/dev/null

1.4 使用别名

别名是一个或多个命令的快捷方式定义。别名的语法为:

alias NAME=DEFINITION

例如,下行定义了一个别名 lt,它会输出一份较长的列表(选项 -l),将其按修改时间排序 (-t),并按排好序的倒序打印 (-r):

tux > alias lt='ls -ltr'

要查看所有别名定义,请使用 alias。使用 unalias 和相应的别名删除别名。

1.5 在 Bash 中使用变量

外壳变量可以是全局变量,也可以是局部变量。全局变量(或环境变量)可以在所有外壳中访问。而局部变量仅在当前外壳中可见。

要查看所有环境变量,请使用 printenv 命令。如果需要知道变量的值,请将变量名称作为参数插入:

tux > printenv PATH

也可以使用 echo 查看变量(无论是全局或本地变量):

tux > echo $PATH

要设置局部变量,请使用变量名后加等号和值:

tux > PROJECT="SLED"

不要在等号两边插入空格,否则会出错。要设置环境变量,请使用 export

tux > export NAME="tux"

要删除变量,请使用 unset

tux > unset NAME

下表包含外壳脚本中可以使用的一些常见环境变量:

表 1.5︰ 有用的环境变量

HOME

当前用户的用户主目录

HOST

当前主机名

LANG

当一个工具本地化后,它使用此环境变量中的语言。英语也可以设置为 C

PATH

外壳的搜索路径,冒号分隔的目录列表

PS1

指定在每个命令前打印的普通提示符

PS2

指定在执行多行命令时打印的辅助提示符

PWD

当前工作目录

USER

当前用户

1.5.1 使用参数变量

例如,如果具有脚本 foo.sh,则可以如下执行:

tux > foo.sh "Tux Penguin" 2000

要访问传递给脚本的所有参数,您需要定位参数。$1 表示第一个参数,$2 表示第二个参数,依此类推。至多可以有九个参数。要获取脚本名称,请使用 $0

以下脚本 foo.sh 打印从 1 到 4 的所有参数:

#!/bin/sh
echo \"$1\" \"$2\" \"$3\" \"$4\"

如果使用上述参数执行此脚本,将获取:

"Tux Penguin" "2000" "" ""

1.5.2 使用变量替代项

变量替换将一个模式应用于变量的内容(从左侧或从右侧)。以下列表包含可能的语法格式:

${VAR#pattern}

从左侧删除可能的最短匹配:

tux > file=/home/tux/book/book.tar.bz2
tux > echo ${file#*/}
home/tux/book/book.tar.bz2
${VAR##pattern}

从左侧删除可能的最长匹配:

tux > file=/home/tux/book/book.tar.bz2
tux > echo ${file##*/}
book.tar.bz2
${VAR%pattern}

从右侧删除可能的最短匹配:

tux > file=/home/tux/book/book.tar.bz2
tux > echo ${file%.*}
/home/tux/book/book.tar
${VAR%%pattern}

从右侧删除可能的最长匹配:

tux > file=/home/tux/book/book.tar.bz2
tux > echo ${file%%.*}
/home/tux/book/book
${VAR/pattern_1/pattern_2}

将来自 PATTERN_1VAR 的内容替代为 PATTERN_2

tux > file=/home/tux/book/book.tar.bz2
tux > echo ${file/tux/wilber}
/home/wilber/book/book.tar.bz2

1.6 分组和组合命令

外壳允许您对命令执行连接和分组以有条件地执行。每个命令都返回一个退出码,该退出码确定操作是成功还是失败。如果是 0,则命令成功,任何其他值都表示特定于该命令的一个错误。

以下列表显示可以如何将命令分组:

命令 1 ; 命令 2

顺序地执行这些命令。不检查退出码。以下行使用 cat 显示文件的内容,然后使用 ls 打印其文件属性,而不考虑退出码:

tux > cat filelist.txt ; ls -l filelist.txt
命令 1 && 命令 2

如果左侧命令成功,则运行右侧命令(逻辑运算符 AND)。仅当上一个命令成功时,以下行才显示文件的内容并打印其文件属性(将其与列表中的上一项相比较):

tux > cat filelist.txt && ls -l filelist.txt
命令 1 || 命令 2

当左侧命令失败时运行右侧命令(逻辑运算符 OR)。以下行仅当在 /home/tux/foo 中创建目录失败时才会在 /home/wilber/bar 中创建目录:

tux > mkdir /home/tux/foo || mkdir /home/wilber/bar
funcname(){ ... }

创建外壳函数。您可以使用定位参数访问其参数。以下行定义用于打印短消息的函数 hello

tux > hello() { echo "Hello $1"; }

您可以如下调用此函数:

tux > hello Tux

它会打印:

Hello Tux

1.7 使用通用流程构造

为了控制脚本的流程,外壳有 whileifforcase 等构造语句。

1.7.1 if 控制命令

if 命令用于检查表达式。例如,以下代码测试当前用户是否是 Tux:

if test $USER = "tux"; then
  echo "Hello Tux."
else
  echo "You are not Tux."
fi

测试表达式既可以复杂也可以简单。以下表达式检查文件 foo.txt 是否存在:

if test -e /tmp/foo.txt ; then
  echo "Found foo.txt"
fi

测试表达式也可以缩写在方括号中:

if [ -e /tmp/foo.txt ] ; then
  echo "Found foo.txt"
fi

https://bash.cyberciti.biz/guide/If..else..fi 上可以找到更多有用表达式。

1.7.2 使用 for 命令创建循环

for 循环允许您对一系列项执行命令。例如,以下代码打印关于当前工作目录中 PNG 文件的某些信息:

for i in *.png; do
 ls -l $i
done

1.8 更多信息

关于 Bash 的重要信息在手册页 man bash 中提供。可以在以下列表中找到关于此主题的更多信息: