一、 提高命令行运行效率
1. 编写简单的 Bash 脚本
1.1 创建和执行 shell 脚本
- 脚本的第一行以符号
# !开头,此符号是一个解释器指令,指示处理文件的其余行所需的命令解释器和、命令选项。对于 Bash 语法脚本文件,第一行就是以下指令:#!/bin/bash。 - shell 脚本必须为可执行文件,使用 chmod 命令可添加执行权限。
- 可将脚本放在 shell 的 PATH 环境变量列出的目录中,即可直接输入脚本名来运行,示例:
[user@host ~]$ which hello /bin/hello [user@host ~]$ echo $PATH /home/user/.local/bin:/home/user/bin:/usr/share/Modules/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin - 或使用
./在当前工作目录中运行脚本,例如./scriptname。
1.2 对特殊字符加引号
- 一些字符和词语对 Bash shell 具有特殊含义,有时希望使用这些字符的字面值,而非其特殊含义。 以下三种工具之一可以取消(或转义)特殊含义:反斜杠(\);单引号(‘ ’);双引号(" "),示例:一-第三章-5-防止参数被扩展
[user@host ~]$ echo #not a comment# [user@host ~]$ echo \#not a comment# #not a comment# [user@host ~]$ echo \#not a comment\# #not a comment# [user@host ~]$ echo '#not a comment#' #not a comment#
1.3 从 Shell 脚本提供输出
- 通过将文本作为参数传递给命令,echo 命令可显示任意文本,默认情况下,文本将在标准输出(STDOUT)上显示,示例:
[user@host ~]$ cat /bin/hello #!/bin/bash echo "Hello,world" [user@host ~]$ hello(命令) Hello,world
/bin/hello 是一个可执行脚本:创建的 hello 脚本位于 /bin/hello,并且已经具有可执行权限(假设已经进行了 chmod +x /bin/hello),所以可以直接在命令行输入 hello 来执行它。
$PATH 是一个环境变量,其中列出了操作系统查找可执行命令时需要遍历的目录。当在命令行输入命令时,系统会在 $PATH 中列出的所有目录中查找对应的可执行文件。如果找到了就执行它。
hello 命令可以直接运行,是因为它位于系统的 /bin 目录下,而 /bin 目录已经包含在 $PATH 环境变量中。
如果将脚本放在其他目录下(比如当前目录),需要使用 ./hello 来执行它,因为当前目录(./)通常不在 $PATH 中。
/bin 是 $PATH 中的一个目录,所以当输入 hello 时,系统会在 /bin 目录下找到 hello 脚本并执行它。
- 也可以使用输出重定向将其定向到标准错误(STDERR),示例:
[user@host ~]$ cat /bin/hello #!/bin/bash echo "Hello,world" echo "ERROR:Houston,we have a problem.">&2 [user@host ~]$ hello 2> hello.log Hello,world [user@host ~]$ cat hello.log ERROR:Houston,we have a problem.
>&2: 这部分是将 标准输出(stdout) 重定向到 标准错误(stderr),即 echo 输出的内容会发送到错误流(stderr)而不是常规的输出流(stdout)。但它并不意味着命令本身出错。它只是告诉系统:“虽然这不是错误信息,但我希望它走错误流。”
2. 脚本中的循环和条件结构
2.1 使用循环来迭代命令
- Bash 的 for 循环结构使用以下语法:
for VARIABLE in LIST; do COMMAND $VARIABLE; done,示例:[user@host ~]$ for HOST in host1 host2 host3; do echo $HOST; done host1 host2 host3 [user@host ~]$ for HOST in host{1,2,3}; do echo $HOST; done host1 host2 host3 [user@host ~]$ for HOST in host{1..3}; do echo $HOST; done host1 host2 host3 [user@host ~]$ for FILE in file*; do ls $FILE; done filea fileb filec [user@host ~]$ for FILE in file{a..c}; do ls $FILE; done filea fileb filec [user@host ~]$ for PACKAGE in $(rpm -qa | grep kernel); do echo "$PACKAGE was installed on $(date -d @$(rpm -q --qf %{INSTALLTIME}n $PACKAGE))"; done abrt-addon-kernelops-2.1.11-12.el7.x86_64 was installed on Tue Apr 22 00:09:07 EDT 2014 kernel-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:27:52 EDT 2014 kernel-tools-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:28:01 EDT 2014 kernel-tools-libs-3.10.0-121.el7.x86_64 was installed on Thu Apr 10 15:26:22 EDT 2014 [user@host ~]$ for EVEN in $(seq 2 2 10); do echo "$EVEN"; done 2 4 6 8 10
2.2 在脚本使用退出代码
- 在处理完所有内容后,脚本会退出到调用它的进程,但是,有时候可能需要在完成之前退出脚本,比如在遇到错误条件时,可在脚本中使用 exit 命令来实现这一目的。
- 可使用整数参数(0 到 255 之间,表示退出代码)来执行 exit 命令,退出代码是进程完成后返回的代码,0 表示没有错误;非零值都表示存在错误。
- 可以使用不同的非零值来区分遇到的不同类型错误,此退出代码传回到父进程,并存储在
?变量中,可通过$?访问退出代码,示例:[user@host bin]$ cat hello #!/bin/bash echo "Hello,world" exit 0 [user@host bin]$ ./hello Hello,world [user@host bin]$ echo $? 0
2.3 测试脚本输入
[ … ] 是条件测试命令(其实是 test 命令的简写)。
它的执行结果(真 / 假)通过特殊变量 $? 表示:
$? = 0 → 表示条件为 真 (True);
$? = 1 → 表示条件为 假 (False)。
- 可通过 test 命令对脚本进行语法测试,示例:
[user@host ~]$ test 1 -ge 0; echo $? 0 [user@host ~]$ test 0 -gt 1; echo $? 1 - 可以使用 Bash 的测试语法
[ <TESTEXPRESSION> ]来执行测试。 - Bash 数字比较运算符,示例:
[user@host ~]$ [ 1 -eq 1 ]; echo $? 0 [user@host ~]$ [ 1 -ne 1 ]; echo $? 1 [user@host ~]$ [ 8 -gt 2 ]; echo $? 0 [user@host ~]$ [ 2 -ge 2 ]; echo $? 0 [user@host ~]$ [ 2 -lt 2 ]; echo $? 1 [user@host ~]$ [ 1 -lt 2 ]; echo $? 0
| 符号 | 含义 |
|---|---|
-eq | 等于(equal) |
-ne | 不等于(not equal) |
-gt | 大于(greater than) |
-lt | 小于(less than) |
-ge | 大于或等于(greater or equal) |
-le | 小于或等于(less or equal) |
- 字符串比较运算符的使用,示例:
[user@host ~]$ [ abc = abc ]; echo $? 0 [user@host ~]$ [ abc == def ]; echo $? 1 [user@host ~]$ [ abc != def ]; echo $? 0
| 符号 | 含义 |
|---|---|
= 或 == | 字符串相等 |
!= | 字符串不相等 |
- Bash 的字符串一元运算符,示例:
[user@host ~]$ STRING=""; [ -z "$STRING" ]; echo $? 0 [user@host ~]$ STRING='abc'; [ -n "$STRING" ]; echo $? 0
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
-z "$str" | 字符串为空 | "" → ✅ | $?=0 |
-n "$str" | 字符串非空 | "abc" → ✅ | $?=0 |
| 判断语句 | 含义 | $? 结果 |
|---|---|---|
[ 条件 ] | 判断条件是否为真 | 0=真, 1=假 |
[ 2 -lt 3 ] | 数值比较 | 0 |
[ abc = def ] | 字符串比较 | 1 |
[ -z "$x" ] | 是否为空字符串 | 0(空时真) |
2.4 if/then 条件判断结构
- Bash 中最简单的条件结构是 if/then 结构,其语法如下:
if <CONDITION>; then <STATEMENT> <STATEMENT> fi - 如果满足给定条件,将采取一个或多个操作,如果不满足给定条件,则不采取任何操作,示例:
[user@host ~]$ systemctl is-active psacct >/dev/null 2>&1 [user@host ~]$ if [ $? -ne 0 ]; then sudo systemctl start psacct; fi
2.5 if/then/else 结构
- if/then 结构进一步扩展,以便能根据是否满足条件来采取不同的操作集合,语法如下:
if <CONDITION>; then <STATEMENT> <STATEMENT> else <STATEMENT> <STATEMENT> fi - 示例:
[user@host ~]$ systemctl is-active psacct >/dev/null 2>&1 [user@host ~]$ if [ $? -ne 0 ]; then \ sudo systemctl start psacct; \ else \ sudo systemctl stop psacct; fi
2.6 if/then/elif/then/else 结构
- if/then/else 结构可以进一步扩展以测试多个条件,在满足某个条件时执行不同的操作集合,语法如下:
if <CONDITION>; then <STATEMENT> <STATEMENT> elif <CONDITION>; then <STATEMENT> … <STATEMENT> else <STATEMENT> <STATEMENT> fi - 在此条件结构中,Bash 将按照显示的顺序测试条件,在发现某个条件成立后,Bash 将执行与该条件相关联的操作,然后跳过条件结构的其余部分,如果所有条件均不成立,Bash 将执行 else 子句中的操作,示例:
[user@host ~]$ systemctl is-active mariadb >/dev/null 2>&1 MARIADB_ACTIVE=$? [user@host ~]$ sudo systemctl is-active postgresql >/dev/null 2>&1 POSTGRESQL_ACTIVE=$? [user@host ~]$ if [ "$MARIADB_ACTIVE" -eq 0 ]; then mysql; \ elif [ "$POSTGRESQL_ACTIVE" -eq 0 ]; then \ psql; \ else \ sqlite3; \ fi
3. 使用正则表达式匹配文本
3.1 正则表达式
- 正则表达式使用模式匹配机制查找特定内容。
- vim、grep 和 less 命令都可以使用正则表达式。
- Perl、Python 和 C 等编程语言在使用模式匹配条件时,也都会使用正则表达式。
- 正则表达式自成体系,也就是说,该语言有其自身的语法和规则。
3.2 描述正则表达式
-
最简单的正则是:行中完全匹配,举例:
grep cat /usr/share/dict/words。 -
使用
^表示开头匹配,使用$表示末尾匹配,举例:grep ^cat abc.txt;grep dog$ abc.txt。 -
^cat$表示匹配只有 cat 的行,cat开头,cat结尾。 -
c.t表示匹配:含有 cat、c5t、cQt 这样内容的行。 -
c[aou]t表示只匹配:cat、cot、cut。 -
c.*t表示匹配:cat、ct、coat、culvert 等以 c 开头,以 t 结尾的内容。 -
c.\{2\}t表示匹配 c 开头,t 结尾,中间正好 2 个字符的内容,比如 coat,举例:grep 'c.\{2\}t' /usr/share/dict/words。. :匹配任意单个字符
* :匹配前一个符号的“零次或多次”
也就是说,.* 表示“任意长度的字符串(包括空)
| 基本语法 | 扩展语法 | 描述 |
|---|---|---|
| . | 句点 (.) 匹配任何单个字符。 | |
| ? | 几个字符 | |
| * | 对前序项匹配零次或多次。 | |
| + | 对前序项匹配一次或多次。 | |
| {\n} | {n} | 对前序项匹配恰好 n 次。 |
3.3 通过 grep 匹配
- grep 命令后跟一个正则表达式和一个文件,示例:
[user@host ~]$ grep 'computer' /usr/share/dict/words computer computerese computerise computerite computerizable computerization computerize computerized computerizes computerizing computerlike computernik computers - 由于正则表达式常含有 shell 元字符(如 $、* 和 {}),建议使用单引号括起正则表达式。
- 通过竖线运算符(|),grep 命令可与其他命令一起使用,示例:
[root@host ~]# ps aux | grep chrony chrony 662 0.0 0.1 29440 2468 ? S 10:56 0:00 /usr/sbin/chronyd
| 元字符 | 在 Shell 中 的意义 | 在 正则中 的意义 | 解决办法 |
|---|---|---|---|
* | 通配符:匹配文件名任意长度 | 前一个字符重复 0 次或多次 | 用引号 'c.*t' |
$ | 取变量值(如 $HOME) | 匹配行尾 | 用单引号 'abc$' |
{} | 花括号扩展 {1,2} | 限定重复次数 {2,5} | 在基本正则中要写成 \{2,5\} |
[] | Shell 通配符集 [abc] | 正则字符集 [abc] | 都一样,但仍建议加引号 |
() | 子 Shell、命令分组 | 正则分组(在 ERE) | 用 \( \) 或 -E |
| ` | ` | 管道符 | 正则“或”运算 |
? | 通配符(单字符) | 前一字符重复 0 或 1 次 | 用引号 |
\ | 转义符 | 转义符 | 在 Shell 中要写两次 \\ |
3.4 grep 选项
| 选项 | 功能 |
|---|---|
| -i | 使用所提供的正则表达式,但不会强制区分大小写(运行不区分大小写的操作) |
| -v | 仅显示不包含正则表达式匹配项的行,取反,不匹配 |
| -r | 将递归地匹配正则表达式的数据搜索应用到一组文件或目录中 |
| -A NUMBER | 显示正则表达式匹配项之后的行数 |
| -B NUMBER | 显示正则表达式匹配项之前的行数 |
| -e | 如果使用多个 -e 选项,则可以提供多个正则表达式,并将与逻辑 OR 一起使用 |
- 示例 1:
[user@host ~]$ grep -i serverroot /etc/httpd/conf/httpd.conf # with "/", the value of ServerRoot is prepended -- so 'log/access_log' # ServerRoot at a non-local disk, be sure to specify a local disk on the # with ServerRoot set to '/www' will be interpreted by the # ServerRoot: The top of the directory tree under which the server's # same ServerRoot for multiple httpd daemons, you will need to change at ServerRoot "/etc/httpd" - 示例 2:
[user@host ~]$ grep -v -i server /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 172.25.254.254 classroom.example.com classroom 172.25.254.254 content.example.com content 172.25.254.254 materials.example.com materials 172.25.250.254 workstation.lab.example.com workstation ### rht-vm-hosts file listing the entries to be appended to /etc/hosts 172.25.250.254 workstation.lab.example.com workstation - 示例 3:
[user@host ~]$ grep -v '[#;]' /etc/ethertypes 0800 ip IPv4 0805 X25 0806 arp ARP 0808 FR_ARP #Frame Relay ARP [RFC1701] # Internet IP (IPv4) ip4 0800 ether-arp 0806
‘[#;]’ 是一个正则字符类
[…] 表示“匹配方括号中任意一个字符”
[ # ; ] 表示匹配:
#(井号)或 ;(分号)
也就是:
“匹配任何包含 # 或 ; 的行”
grep -v 的意思是:
显示 不包含 正则匹配内容的行。
也就是排除掉含有 # 或 ; 的行。
/etc/ethertypes 是系统文件
/etc/ethertypes 通常包含 以太网协议类型编号
- 示例 4:
[root@host ~]# cat /var/log/secure | grep -e 'pam_unix' -e 'user root' -e 'Accepted publickey' | less Mar 19 08:04:46 jegui sshd[6141]: pam_unix(sshd:session): session opened for user root by (uid=0) Mar 19 08:04:50 jegui sshd[6144]: Disconnected from user root 172.25.250.254 port 41170 Mar 19 08:04:50 jegui sshd[6141]: pam_unix(sshd:session): session closed for user root Mar 19 08:04:53 jegui sshd[6168]: Accepted publickey for student from 172.25.250.254 port 41172 ssh2: RSA SHA256:M8ikhcEDm2tQ95007vufqEixcFct+WOwZLNZN1BTO - 在 vim 或 less 命令中进行搜索时,输入
/字符,然后键入搜索内容,按 Enter 开始搜索,按 N 查找下一个匹配项,示例:[root@host ~]# vim /var/log/boot.log ...output omitted... [[0;32m OK [0m] Finished [0;1;39mdracut pre-pivot and cleanup hook[0m. [[0;32m OK [0m] Stopped target [0;1;39mRemote Encrypted Volumes[0m. [[0;32m OK [0m] Starting [0;1;39mCleaning Up and Shutting Down Daemons[0m... [[0;32m OK [0m] Stopped target [0;1;39mTimer Units[0m. [[0;32m OK [0m] Closed [0;1;39mSystem Message Bus Socket[0m. ...Daemons
816

被折叠的 条评论
为什么被折叠?



