Shell 脚本 — 多行注释、开启子/不开启子进程执行、转义带颜色输出、读取键盘输入、输入输出重定向、单双引号、命令替换、读取变量、系统变量、正则过滤、算术运算、一行多条命令、字符串比较

1. 多行注释

#!/bin/bash

<< COMMENT
This is multi
line comment
COMMENT

echo "This is demo script"

注意:

<<符号后面的关键词可以是任意字符串,但是前面使用什么关键词,结束注释时必须使用同样的关键词,如果使用 << ABC 开始注释,那么结束注释时必须使用 ABC,字母区分大小写。

2. 执行方式

默认情况下,创建脚本之后是无法直接运行的:

$ ./demo.sh
-bash: ./demo.sh: Permission denied

bash 或者 sh 这样的解释器,是可以将脚本文件作为参数来执行脚本文件的。

$ bash demo.sh
This is demo script
$ sh demo.sh
This is demo script

修改脚本具有可执行权限,然后就可以使用相对路径或者绝对路径来执行了。

$ chmod +x demo.sh
$ ./demo.sh
This is demo script
$ /home/wohu/Downloads/shell_demo/demo.sh
This is demo script
$

2.1 开启子进程

输入 pstree,可以看到

$ pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ├─NetworkManager─┬─dhclient
        │                └─2*[{NetworkManager}]
...
        ├─sshd─┬─2*[sshd───sshd───bash]
        │      ├─sshd───sshd───bash───pstree
        │      ├─sshd───sshd───sftp-server
        │      └─sshd───sshd───bash───ssh
...

可以看到系统启动的第一个进程是 systemd,其它进程都是这个进程的子进程,不管是直接执行脚本,还是通过 bash 或者 sh 这样的解释器执行脚本,都是会开启子进程的,下面验证一下,

创建下面的文件内容并通过 ./sleep.sh 执行该脚本文件

$ cat sleep.sh
#!/bin/bash
sleep 100

然后输入 pstree 可以看到


        ├─sshd─┬─2*[sshd───sshd───bash]
        │      ├─sshd───sshd───bash───sleep.sh───sleep
        │      ├─2*[sshd───sshd───sftp-server]
        │      ├─sshd───sshd───bash───pstree
        │      └─sshd───sshd───bash───ssh

bash 终端下开启了一个子进程脚本文件 sleep.sh,通过该脚本文件执行了一条 sleep 命令。

另外一种方法,使用 bash sleep.sh执行,然后输入 pstree 可以看到

        ├─sshd─┬─2*[sshd───sshd───bash]
        │      ├─sshd───sshd───bash───bash───sleep
        │      ├─2*[sshd───sshd───sftp-server]
        │      ├─sshd───sshd───bash───pstree
        │      └─sshd───sshd───bash───ssh

bash 进程下开启了一个子进程 bash,在 bash 子进程中执行了一条 sleep 命令。

2.2 不开启子进程

打开终端使用 source 或者 . 来执行脚本文件

$ source  sleep.sh

或者

. sleep.sh

另外开个终端,然后输入 pstree 可以看到

        ├─sshd─┬─2*[sshd───sshd───bash]
        │      ├─sshd───sshd───bash───sleep
        │      ├─2*[sshd───sshd───sftp-server]
        │      ├─sshd───sshd───bash───pstree
        │      └─sshd───sshd───bash───ssh

脚本文件中的 sleep 命令是直接在 bash 终端下执行的。

特殊的文件命令如下

$ cat exit.sh
#!/bin/bash
exit

分别使用开启子进程和不开启子进程的方式执行,

$ source exit.sh
$ bash exit.sh

可以发现:

  • source 命令不开启子进程执行脚本文件会导致整个终端被关闭;
  • bash 命令开启子进程执行脚本文件却不受影响;

3. 数据输入和输出

3.1 使用 echo 命令输出

echo 命令可以实现任意字符串消息的输出,可以使用多个 echo 输出多条消息,也可以使用一个 echo 命令,将多条信息用引号 "" 包含一起输出。

$ cat demo.sh
#!/bin/bash

echo "one"
echo "two"
echo "This is
demo script"

echo 命令支持 -e 选项,该选项可以让 echo 命令识别 \ 后面的转义符含义,常见的转义符见下表:
转义字符

注意: -e 选项和后面的输出内容至少要有一个空格。

echo -e "\033[1mok\033[0m"

\033 或者 \e 后面跟不同的代码可以设置不同的终端属性,如 1m 是让终端粗体显示字符串、后面的 OK 就是需要显示的字符串内容,最后的 \033[0m 是在终端加粗显示字符串后,关闭终端的属性设置,如果没有使用 0m 关闭属性设置,则之后终端所有字符串都用粗体显示。

颜色显示
使用示例:

#!/bin/bash
#Version:2.0
clear
echo -e "\033[42m---------------------------------\033[0m"
echo -e "\e[2;10H这里是菜单\t\t#"
echo -e "#\e[32m 1.查看网卡信息\e[0m                #"
echo -e "#\e[33m 2.查看内存信息\e[0m                #"
echo -e "#\e[34m 3.查看磁盘信息\e[0m                #"
echo -e "#\e[35m 4.查看CPU信息\e[0m                 #"
echo -e "#\e[36m 5.查看账户信息\e[0m                #"
echo -e "\033[42m---------------------------------\033[0m"
echo

3.2 使用 read 命令读取

read命令语法如下:

read [选项] [变量名]

如果未指定变量名,则变量名默认为 REPLY
read
read-2
passed

4. 输入输出重定向

Linux 系统输出可以分为标准输出和标准错误输出。

  • 标准输出的文件描述符为 1
  • 标准错误输出的文件描述符为 2
  • 标准输入的文件描述符为 0

可以使用 > 或者 >> 符号将输出信息重定向到文件中。

  • > 符号将输出信息重定向到文件,如果文件不存在,则系统会自动创建该文件,如果文件已经存在,则系统会将文件的所有内容覆盖;
  • >> 符号将输出信息重定向到文件,如果文件不存在,则系统会自动创建该文件,如果文件已经存在,则系统会将输出的信息追加到该文件原有信息的末尾;

使用 1 > 或者 1 >> 可以将标准输出信息重定向到文件 (1 可以忽略不写,默认值就是 1)。可以使用 2 > 或者 2 >> 将错误的输出信息重定向到该文件。

重定向
在
22
33
a
b

3

使用 << 符号可以将数据内容重定向传递给前面的一个命令,作为命令的输入。<< 符号代表需要的内容在这里。

[wohu@bogon Videos]$ cat >  demo.sh << EOF
this is demo
EOF
[wohu@bogon Videos]$ cat demo.sh 
this is demo
[wohu@bogon Videos]$ 
[wohu@bogon Videos]$ cat >  demo.sh << HERE
this is test script
HERE
[wohu@bogon Videos]$ cat demo.sh 
this is test script
[wohu@bogon Videos]$ 

5. 各种引号的使用

5.1 单双引号

$ touch a b c 		  # 创建三个文件,分别为 a b c
$ touch "a b c" 		# 创建一个文件,空格为文件名的一部分
		

双引号的作用是引用一个整体,计算机会把整个引号内的内容当做一个整体来看待。

Linux系统中,除了可以使用一个双引号引用一个整体,也可以使用单引号引用一个整体,同时单引号还有另外一个功能是将特殊字符含义屏蔽,转化为字符表面的含义。

$ touch 'a b c'
$ echo ############

本来想输出一个 #符号,但实际输出的是一个空白行,因为 #符号及其后面的内容会被理解为注释而不会执行,所以要想输出 #符号,可以使用单引号 '###'

$ echo '########'

Shell$符号具有提取变量值的特殊含义,当需要显示 $符号时,也需要使用单引号屏蔽功能。

wohu@wohu-pc:~$ test=19
wohu@wohu-pc:~$ echo "$test"
19
wohu@wohu-pc:~$ echo '$test'
$test
wohu@wohu-pc:~$

5.2 命令替换

反引号 ` 是一个命令替换符号,它可以使用命令的替换结果替代命令。

wohu@wohu-pc:~$ echo `date +%Y-%m-%d`
2022-07-27
wohu@wohu-pc:~$
wohu@wohu-pc:~$ echo `ls | wc -l`
16
wohu@wohu-pc:~$ 

反引号容易跟单引号混淆,并且不支持嵌套,所以又有了 $()组合命令,同样用于命令替换。

wohu@wohu-pc:~$ echo $(pwd)
/home/weirong
wohu@wohu-pc:~$ echo $(ls | wc -l)
16
wohu@wohu-pc:~$ echo $(date +%Y-%m-%d)
2022-07-27
wohu@wohu-pc:~$

6. 变量

变量名由数字、字母、下划线组成,且开头不能以数字开头,定义变量时等号两边不可以有空格。
当需要读取变量时,需要在变量名前加一个 $符号,且变量名与其它非变量名混在一起时,需要使用 {}分隔。

wohu@wohu-pc:~$ a = 123
a: command not found
wohu@wohu-pc:~$ a=123
wohu@wohu-pc:~$ echo $a
123
wohu@wohu-pc:~$ echo $(a)   # $() 用于命令替换
a: command not found

wohu@wohu-pc:~$ echo ${a}		# ${} 用于引用变量
123
wohu@wohu-pc:~$ echo $aapples

wohu@wohu-pc:~$ echo ${a}apples
123apples
wohu@wohu-pc:~$

脚本

#!/bin/bash
#描述信息:本脚本主要目的是获取主机的数据信息(内存、网卡IP、CPU负载)

localip=$(ifconfig eth0 | grep netmask | tr -s " " | cut -d" " -f3)
mem=$(free |grep Mem |tr -s " " | cut -d" " -f7)
cpu=$(uptime | tr -s " " | cut -d" " -f13)
echo "本机IP地址是:$localip"
echo "本机内存剩余容量为:$mem"
echo "本机CPU 15分钟的平均负载为:$cpu"
  • tr -s " " 将多个空格合并为一个
  • cut -d" " -f3以空格分列,并输出第三列

常见系统预设变量有:
env

7. 数据过滤与正则表达

grep [选项] 匹配模式 [文件]
  • -i 忽略字母大小写
  • -v 取反匹配
  • -w 匹配单词

grep

8. 算术运算

Shell支持多种算术运算,可以使用 $((表达式))$[表达式]let 表达式 进行整数算术运算,使用 bc 命令进行小数运算。

wohu@wohu-pc:~$ echo $((3+4))
7
wohu@wohu-pc:~$ echo $[3+4]
7
wohu@wohu-pc:~$ a=3
wohu@wohu-pc:~$ echo $[a++]
3
wohu@wohu-pc:~$ echo $[a++]
4
wohu@wohu-pc:~$ echo $[++a]
6
wohu@wohu-pc:~$

运算符号见下表:
算术运算

9. 一行多条命令

一行代码中输入多行 shell 命令,可以用分号 ;,与号 && ,或 || 将多个命令分开。

  • cmd1 ; cmd2 先执行 cmd1,再执行 cmd2,不管 cmd1 执行结果如何,整个命令的退出码以 cmd2 为准;
  • cmd1 && cmd2 仅当 cmd1 执行成功后,才会执行 cmd2,两条命令都执行成功,则整行命令的退出码为 0,有任意一个执行失败,整行命令的退出码为非 0;
  • cmd1 || cmd2 一般很少用,只有当 cmd1 不执行或者执行失败时才会执行 cmd2cmd1cmd2 是二选一的关系,两个中的任意一个执行成功,那么整条命令的退出码为 0,否则返回非 0;

10. 字符串比较

wohu@ubuntu:~$ test a == a
wohu@ubuntu:~$ echo $?
0
wohu@ubuntu:~$ test a == b; echo $?
1
wohu@ubuntu:~$ [ $USER == wohu ] && echo Y || echo N
Y
wohu@ubuntu:~$ [ $USER != wohu ] && echo Y || echo N
N
wohu@ubuntu:~$

表达式 -z 测试字符串是否为空。

wohu@ubuntu:~$ a=123
wohu@ubuntu:~$ [ -z $a ] && echo Y || echo N
N
wohu@ubuntu:~$ [ -z $b ] && echo Y || echo N
Y
wohu@ubuntu:~$

Shell 中进行条件测试时一定要注意空格问题,使用 [] 测试时,左括号右边和右括号左边都必须要有空格,而且测试的比较符号两边也必须都有空格。

wohu@ubuntu:~$ [a == city] # 前后都缺少空格,报错
[a: command not found
wohu@ubuntu:~$

下面的例子,== 符号两边都没有空格,无论怎么测试结果都为真,编写脚本时这种 Bug 系统不会提示语法错误,但是程序结果有可能是错误的。

wohu@ubuntu:~$ [ a==city ]; echo $?				# 退出码是错误的
0
wohu@ubuntu:~$ [ a == city ]; echo $?
1
wohu@ubuntu:~$

还可以使用 -n 测试一个字符串是否为非空,在实际应用时最好将测试对象使用双引号引起来。否则当测试一个未定义的变量时就会出现下面的问题。

wohu@ubuntu:~$ [ -n $str ]; echo $?
0
wohu@ubuntu:~$ 

因为当 $str 为空时,等同于执行了下面的第一条命令,是在测试一个空格是否为空值。而计算机理解空格也是有值的,并非没有值(空值),所以这样的测试结果总为真。

wohu@ubuntu:~$ [ -n ] && echo Y  || echo N
Y
wohu@ubuntu:~$

为了避免这样的错误,可以使用双引号将变量引起来。

wohu@ubuntu:~$ [ -n "$str" ]; echo $?
1
wohu@ubuntu:~$ [ -n "$str"] && echo Y  || echo N  # 右方括号前没有空格,导致结果错误,切记
Y
wohu@ubuntu:~$ [ -n "$str" ] && echo Y  || echo N
N
wohu@ubuntu:~$

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值