Shell 编程笔记
目录
1. 安装欧拉系统
2. 回顾常用命令
2.1 设置主机名
要设置主机名称,我们需要使用如下的命令格式来完成:
hostnamectl set-hostname 主机名称
例如:将默认的主机名称(localhost)修改为 openEuler
# 修改主机名称
[root@localhost ~]# hostnamectl set-hostname openEuler
# 查看主机名
[root@localhost ~]# hostnamectl hostname
openEuler
# 查看保存主机名的文件内容
[root@localhost ~]# cat /etc/hostname
openEuler
[root@localhost ~]#
2.2 nmcli命令
我们修改虚拟机的IP地址为 150,需要使用 nmcli 命令
格式:
Usage: nmcli [OPTIONS] OBJECT {
COMMAND | help }
1)查看网卡名称
[root@localhost ~]# ip a
[root@localhost ~]# ifconfig
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:4f:96:ec brd ff:ff:ff:ff:ff:ff
inet 192.168.72.133/24 brd 192.168.72.255 scope global dynamic noprefixroute ens32
valid_lft 1605sec preferred_lft 1605sec
inet6 fe80::20c:29ff:fe4f:96ec/64 scope link noprefixroute
valid_lft forever preferred_lft forever
2)设置IP地址为150
[root@localhost ~]# nmcli c modify ens32 ipv4.addresses 192.168.72.150/24 ipv4.method manual ipv4.dns 114.114.114.114
[root@localhost ~]# nmcli c modify ens32 ipv4.gateway 192.168.72.2
[root@localhost ~]# nmcli c modify ens32 autoconnect yes
[root@localhost ~]# nmcli c up ens32
如果要添加一个新IP而不修改原来的,则执行如下的命令:
[root@openEuler ~]# nmcli c modify ens32 +ipv4.addresses 192.168.72.151/24
[root@openEuler ~]# nmcli c up ens32
2.3 lscpu
这个命令的作用是显示虚拟机的CPU详细信息。
[root@openEuler ~]# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 45 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Vendor ID: AuthenticAMD
BIOS Vendor ID: AuthenticAMD
Model name: AMD Ryzen 7 5800H with Radeon Graphics
BIOS Model name: AMD Ryzen 7 5800H with Radeon Graphics
CPU family: 25
Model: 80
Thread(s) per core: 1
Core(s) per socket: 2
Socket(s): 2
Stepping: 0
BogoMIPS: 6387.99
2.4 重定向
在重定向中涉及到以下三个概述:
- 标准输入,它是用数字 0 表示,用于接受从终端输入的内容
- 标准输出,它是用数字 1 表示,用于将内存或文件中的内容输出到终端或文件
- 标准错误输出,它是用数字 2 表示,将执行命令所产生的错误信息进行输出,可以输入到终端或文件
我在工作中,可以会用到以下方式:
- 2>&1,表示:将标准输出和标准错误输出到同一个设备上
- /dev/null,在linux表示一个黑洞
2.4.1 >
这是代表将内容输入到指定文件中,并且是以覆盖的方式来写入内容。
[root@openEuler ~]# cat /etc/passwd | head -5
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@openEuler ~]# cat /etc/passwd | head -5 > passwd
[root@openEuler ~]# ls
anaconda-ks.cfg hehe.txt passwd
[root@openEuler ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@openEuler ~]# cat /etc/passwd | tail -5 > passwd
[root@openEuler ~]# cat passwd
tcpdump:x:72:72::/:/sbin/nologin
systemd-network:x:987:987:systemd Network Management:/:/usr/sbin/nologin
systemd-resolve:x:986:986:systemd Resolver:/:/usr/sbin/nologin
systemd-timesync:x:985:985:systemd Time Synchronization:/:/usr/sbin/nologin
redhat:x:1000:1000:redhat:/home/redhat:/bin/bash
2.4.2 >>
这种方式表示向文件的末尾追加指定的内容。
[root@openEuler ~]# echo haha >> hehe.txt
[root@openEuler ~]# cat hehe.txt
hello world
haha
[root@openEuler ~]# ls >> hehe.txt
[root@openEuler ~]# cat hehe.txt
hello world
haha
anaconda-ks.cfg
hehe.txt
passwd
2.4.3 <
这个表示输入重定向,它的主要功能是将一个文件的内容作为命令的输入。这使得我们能够将文件中的数据传递给某个命令,从而让命令以文件内容作为输入进行操作。
使用格式:
command < input_file
示例1:查看文件内容
假设我们有一个名为 data.txt
的文件,里面存储了一些文本数据。我们可以使用 <
符号将文件的内容传递给 cat
命令,以查看文件的内容:
[root@openEuler ~]# cat hehe.txt
hello world
haha
anaconda-ks.cfg
hehe.txt
passwd
[root@openEuler ~]# cat < hehe.txt
hello world
haha
anaconda-ks.cfg
hehe.txt
passwd
示例 2 - 统计行数
另一个常见的用法是通过 <
符号将文件传递给 wc
命令,以统计文件的行数、字数和字符数:
[root@openEuler ~]# wc -l hehe.txt
5 hehe.txt
[root@openEuler ~]# wc -l < hehe.txt
5
2.4.4 <<
这种方式是将多行文本的内容输入给某个命令。
格式:
command << EOF
这是内容
这也是内容
EOF
使用示例:
[root@openEuler ~]# cat << EOF
> hello
> world
> good
> openEuler
> .
> EOF
hello
world
good
openEuler
.
在上例中,EOF 是我们自定义的分隔符。我们也可以把这个分隔符放到引号:
[root@openEuler ~]# cat << 'EOF'
> redhat
> openlab
> linux
> .
> EOF
redhat
openlab
linux
.
如果希望将多行文本的内容输入到指定的文件中,则可以向如下的方式来实现:
[root@openEuler ~]# cat << EOF > message.txt
> one
> two
> three
> EOF
[root@openEuler ~]# ll
total 16
-rw-------. 1 root root 881 Mar 16 20:10 anaconda-ks.cfg
-rw-r--r--. 1 root root 49 Mar 16 21:07 hehe.txt
-rw-r--r--. 1 root root 14 Mar 17 09:50 message.txt
-rw-r--r--. 1 root root 183 Mar 16 21:03 passwd
[root@openEuler ~]# cat message.txt
one
two
three
[root@openEuler ~]# cat << EOF >> message.txt
> hehe.txt
> message.txt
> EOF
[root@openEuler ~]# cat message.txt
one
two
three
hehe.txt
message.txt
2.4.5 <<<
这个符号是将单行文件内容输入到某个命令中。
格式:
command <<< '单行文本内容'
使用示例:
[root@openEuler ~]# cat <<< 'this is text'
this is text
2.5 wget
wget命令是用于下载网络资源的。它的使用语法:
Usage: wget [OPTION]... [URL]...
常用选项:
-V:查看wget的版本
-b:后台下载
-q:静默下载
-v:显示下载信息
-F:以HTML格式输入文件
-t,--tries=NUMBER:下载的重试次数
-O,--output-document=FILE:将下载的内容写入到文件中
-c,--continue:支持断点续传
-T, --timeout=SECONDS:下载的超时时间
-w, --wait=SECONDS:等待时间
--limit-rate=RATE:限制下载速度
使用示例:
# 查看版本
[root@openEuler ~]# wget -V
GNU Wget 1.21.2 built on linux-gnu.
# 下载文件
[root@openEuler ~]# wget https://pm.myapp.com/invc/xfspeed/qqpcmgr/download/QQPCDownload320059.exe
# 限制下载速度
[root@openEuler ~]# wget https://pm.myapp.com/invc/xfspeed/qqpcmgr/download/QQPCDownload320059.exe
--limit-rate=10K
# 下载后指定文件名称
[root@openEuler ~]# wget https://pm.myapp.com/invc/xfspeed/qqpcmgr/download/QQPCDownload320059.exe -O qq.exe
# 后台下载
[root@openEuler ~]# wget https://pm.myapp.com/invc/xfspeed/qqpcmgr/download/QQPCDownload320059.exe -O qq2.exe -b
2.6 watch
linux命令watch是周期性的用来执行某命令,并把某命令执行结果输出到屏幕上。使用watch命令,可以周期性的监测并输出某命令的执行结果到屏幕上,省得手动一遍一遍运行某命令,提高工作效率。
watch常用参数
参数 | 说明 |
---|---|
-h | watch帮助文档 |
-n | 设置watch时间间隔,默认为2s |
-d | 高亮显示变化的区域 |
-t | 不显示顶部的时间间隔信息 |
示例:
# 第隔 4s 执行一次 ls 命令
[root@openEuler ~]# watch -n 4 ls
2.7 xargs
xargs 命令是给其他命令传递参数的一个过滤器,也是组合多个命令的一个工具。它擅长将标准输入数据转换成命令行参数,xargs 能够处理管道或者 stdin 并将其转换成特定命令的命令参数。
格式:
xargs [选项] [命令]
选项:
-I:用于指定替换字符串,将输入数据中的特定字符串替换为命令行参数。
-n:用于指定每次执行命令的参数个数。
-t:用于打印执行的命令。
-p:用于提示用户确认是否执行命令。
-r:当标准输入为空时,不执行命令。
示例:
# 准备数据
[root@openEuler ~]# cat << EOF > test.txt
>
> a b c d e f g
> h i j k l m n
> o p q
> r s t
> u v w x y z
>
> EOF
[root@openEuler ~]#
[root@openEuler ~]# cat test.txt
a b c d e f g
h i j k l m n
o p q
r s t
u v w x y z
# 多行输入单行输出
[root@openEuler ~]# cat test.txt | xargs
a b c d e f g h i j k l m n o p q r s t u v w x y z
#使用 -n 进行多行输出,-n 选项多行输出:
[root@openEuler ~]# cat test.txt | xargs -n3
a b c
d e f
g h i
j k l
m n o
p q r
s t u
v w x
y z
#使用 -d 分割输入
#-d 选项可以自定义一个定界符:
[root@openEuler ~]# echo "nameXnameXnameXname" | xargs -dX
name name name name
#结合 -n 选项使用:
[root@openEuler ~]# echo "nameXnameXnameXname" | xargs -dX -n1
name
name
name
name
#结合 -I 选项
#xargs 的一个选项 -I,使用 -I 指定一个替换字符串{},这个字符串在 xargs 扩展时会被替换掉,当 -I 与 xargs 结合使用,每一个参数命令都会被执行一次:
cat arg.txt | xargs -I {
} ./sk.sh -p {
} -l
-p aaa -l
-p bbb -l
-p ccc -l
#复制当胶目录下所有.txt文件到当前目录下d1目录中:
[root@openEuler ~]# mkdir d1
[root@openEuler ~]# ll
total 24
-rw-------. 1 root root 881 Mar 16 20:10 anaconda-ks.cfg
drwxr-xr-x. 2 root root 4096 Mar 17 10:28 d1
-rw-r--r--. 1 root root 49 Mar 16 21:07 hehe.txt
-rw-r--r--. 1 root root 35 Mar 17 09:51 message.txt
-rw-r--r--. 1 root root 183 Mar 16 21:03 passwd
-rw-r--r--. 1 root root 54 Mar 17 10:21 test.txt
[root@openEuler ~]# ls *.txt
hehe.txt message.txt test.txt
[root@openEuler ~]# ls *.txt | xargs -I cp {} d1
xargs: {
}: No such file or directory
[root@openEuler ~]# ls *.txt | xargs -n 1 -I {} cp {} d1/
[root@openEuler ~]# ll
total 24
-rw-------. 1 root root 881 Mar 16 20:10 anaconda-ks.cfg
drwxr-xr-x. 2 root root 4096 Mar 17 10:29 d1
-rw-r--r--. 1 root root 49 Mar 16 21:07 hehe.txt
-rw-r--r--. 1 root root 35 Mar 17 09:51 message.txt
-rw-r--r--. 1 root root 183 Mar 16 21:03 passwd
-rw-r--r--. 1 root root 54 Mar 17 10:21 test.txt
[root@openEuler ~]# tree d1
d1
├── hehe.txt
├── message.txt
└── test.txt
0 directories, 3 files
2.8 cron
cron 是 Linux 系统中后台进程模式周期性执行命令或指定程序的服务软件名。Linux 系统启动后,cron 软件便会启动,对应的进程名字叫 crond,默认是定期(每分钟检查一次)检查系统中是否有需要执行的任务计划。如果有则按计划进行,好比我们平时用的闹钟。
crond 服务除了会在工作时查看 /var/spool/cron
文件夹下的定时任务文件以外,还会查看 /etc/cron.d
目录以及 /etc/anacrontab
下面的文件内容,里面存放每天、每周、每月需要执行的系统任务。
[root@openEuler ~]# ls /var/spool/cron/
[root@openEuler ~]# ls -l /etc/ | grep cron*
-rw-r--r--. 1 root root 541 Dec 28 13:43 anacrontab
-rw-------. 1 root root 0 Mar 16 20:17 cron.allow
drwx------. 2 root root 4096 Mar 16 20:07 cron.d
drwx------. 2 root root 4096 Mar 16 20:08 cron.daily
drwx------. 2 root root 4096 Mar 16 20:07 cron.hourly
drwx------. 2 root root 4096 Dec 16 23:57 cron.monthly
-rw-------. 1 root root 451 Dec 16 23:57 crontab
drwx------. 2 root root 4096 Dec 16 23:57 cron.weekly
系统定时任务配置文件 /etc/crontab
[root@openEuler ~]# vim /etc/crontab
# 文件内容如下
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
crontab命令被用来提交和管理用户的需要周期性执行的任务,与 windows 下的计划任务类似。
参数 | 解释 | 示例 |
---|---|---|
-l | list 查看定时任务 | crontab -l |
-e | edit 编辑定时任务,建议手动编辑 | crontab -e |
-i | 删除定时任务,提示用户确认删除,避免出错 | crontab -i |
-r | 删除定时任务,移除/var/spool/cron/username文件 | crontab -r |
-u user | 指定用户执行任务,root 可以管理普通用户计划任务 | crontab -u redhat -l |
使用格式:
* * * * *
分 时 日 月 周
0 * * * * 每小时执行,每小时的整点执行
1 2 * * 4 每周执行,每周四的凌晨2点1分执行
1 2 3 * * 每月执行,每月的3号的凌晨2点1分执行
1 2 3 4 * 每年执行,每年的4月3号的凌晨2点1分执行
1 2 * * 3,5 每周3和周5的2点1分执行
* 13,14 * * 6,0 每周六、周日下午1点和2点的每一分钟都执行
0 9-18 * * 1-5 周一到周五每天早上9点到下午6点的每一个整点
*/10 * * * * 每隔10分钟执行一次任务
注意:如果没法整除,定时任务则没意义,可以自定制脚本控制频率
使用示例:
1. 每分钟执行命令
* * * * * cmd
2. 每天凌晨3点半和12点半执行脚本
30 3,12 * * * cmd
3. 每隔6小时,相当于6,12,18,24点半执行脚本
30 */6 * * * cmd
30 6,12,18,24 * * * cmd
4. 30代表半点,8-18/2表示早上8点到下午18点之间每隔2小时执行脚本
30 8-18/2 * * * cmd
5. 每天晚上9点30重启nginx
30 21 * * * /opt/nginx/sbin/nginx --restart
6. 每月1和10号凌晨4点45执行脚本
45 4 1,10 * * cmd
7. 每周六和周日凌晨1点10分执行命令
10 1 * * 6,7 cmd
10 1 * * 6,0 cmd
8. 每天18点到23点之间,每隔30分钟执行一次
*/30 18-23 * * * cmd
0,30 18-23 * * * cmd
9. 每隔一小时执行一次
* */1 * * * cmd
10. 4月份的周一到周三的上午11点执行脚本
* 11 * 4 1-3 cmd
11. 每天早上7点到上午11点,每2小时运行cmd命令
* 7-11/2 * * * cmd
12. 每天6点执行脚本
0 6 * * * cmd
13. 每周六凌晨4点执行
* 4 * * 6 cmd
14. 每周六凌晨4点05执行
5 4 * * 6 cmd
15. 每天8:40执行
40 8 * * * cmd
16. 在每天10:31开始,每隔2小时重复一次
31 10/2 * * * cmd
17. 每周一到周五2:00执行
0 2 * * 1-5 cmd
18. 每周一到周五8:00,每周一到周五9:00执行
0 8,9 * * 1-5 cmd
19. 每天10:00,16:00执行
0 10,16 * * * cmd
3. Shell脚本基础
3.1 Shell的基本元素
声明:使用那一种解释器来解释并执行当前的脚本。#!/bin/bash
命令:可执行的语句,实现程序的功能。
注释:单行注释,使用 # 号来进行注释,另一种是多行注释 :<<BLOCK 注释内容 BLOCK
赋予脚本可执行权限
3.2 Shell脚本规范
- 脚本名称尽量做见名知意
- 在脚本的开头指定解释器
- 在开头增加版权信息
- 尽量使用系统命令,而少使用管道
- 尽量不要使用中文注释
- 代码注重缩进
3.3 Shell脚本编写方式
-
交互式执行
[root@openEuler ~]# for name in `ls /etc` > do > echo $name > done
-
作为程序文件执行(推荐使用)
[root@openEuler ~]# vim for_test.sh #!/bin/bash echo $USER
3.4 Shell脚本执行方式
bash for_test.sh
命令来执行,它会产生一个子 Shell,然后在子 Shell 中运行脚本,执行完后回去父Shell。sh for_test.sh
命令来执行,它也会产生一个子 Shell,然后在子 Shell 中运行脚本,执行完后回去父Shell。./for_test.sh
命令来执行,它也会产生一个子Shell,不同点在于它需要有 x 权限。. for_test.sh
命令来执行,使用是文件中定义的解释器,不会产生子 Shell,它是在当前的环境中执行的。source for_test.sh
命令来执行,它的作用与 . 执行一样
[root@openEuler ~]# sh for_test.sh
root
[root@openEuler ~]# ll
total 32
-rw-------. 1 root root 881 Mar 16 20:10 anaconda-ks.cfg
drwxr-xr-x. 2 root root 4096 Mar 17 10:29 d1
-rw-r--r--. 1 root root 24 Mar 23 14:32 for_test.sh
-rw-r--r--. 1 root root 49 Mar 16 21:07 hehe.txt
-rw-r--r--. 1 root root 35 Mar 17 09:51 message.txt
-rwxr-xr-x. 1 root root 26 Mar 17 11:18 my.sh
-rw-r--r--. 1 root root 183 Mar 16 21:03 passwd
-rw-r--r--. 1 root root 54 Mar 17 10:21 test.txt
[root@openEuler ~]# ./for_test.sh
-bash: ./for_test.sh: Permission denied
[root@openEuler ~]# chmod a+x for_test.sh
[root@openEuler ~]# ./for_test.sh
root
[root@openEuler ~]# . for_test.sh
root
[root@openEuler ~]# source for_test.sh
root
3.5 Shell退出状态
在 Linux 中执行命令后,会有一个状态,如果值为 0 表示之前执行的命令是正常执行的,如果是非0则表示前一条命令执行是有错误发生。
[root@openEuler ~]# source for_test.sh
root
[root@openEuler ~]# echo $?
0
[root@openEuler ~]# source for_test.sh1
-bash: for_test.sh1: No such file or directory
[root@openEuler ~]# echo $?
1
4. 变量和引用
4.1 什么是变量
变量就是程序设计中一个可以变化的量。它会在内存中开辟一个空间,变量的名称引用是这个空间对应的地址,而这个地址中存储的就是变量的值。
4.2 变量的命名
在 Shell 中变量的名称可以由字母、数字和下划线组成,但是数字不能开头。原则上对变量的长度没有限制,但是一般不推荐变量名太长。尽量做到变量名称见名知意。
4.3 变量的作用范围
在 Shell 中命令的作用范围分为全局变量和局部变量,全局变量也叫系统变量,它在整个系统都可以用。而局部变量是需要在特定的环境中才可以使用的。
4.3.1 局部变量
局部变量也叫用户变量,或者叫临时变量。它只能在当前用户所在的环境中使用,或者在当前的会话中使用。
[root@openEuler ~]# name=lisi
[root@openEuler ~]# echo $name
lisi
[root@openEuler ~]# echo ${name}
lisi
# 产生一个子Shell环境
[root@openEuler ~]# bash
[root@openEuler ~]# echo $name
# 退出到父Shell环境
[root@openEuler ~]# exit
exit
[root@openEuler ~]# echo ${name}
lisi
4.3.2 全局变量
全局变量也叫系统变量,也可以称为永久变量。它是不区分用户环境的,对整个系统都有效。它需要定义在 /etc/profile
或者 /etc/bashrc
文件中通过 export 来导出。
我们在 /etc/profile 中定义一个变量,编写这个文件,在这个文件的最后添加如下内容:
[root@openEuler ~]# vim /etc/profile
export MyPath=/root
然后保存退出,然后测试是否能获取到定义的变量
[root@openEuler ~]# echo $MyPath
[root@openEuler ~]# . /etc/profile
[root@openEuler ~]# echo $MyPath
/root
# 切换用户及环境后再测试
[root@openEuler ~]# su - redhat
[redhat@openEuler ~]$ echo $MyPath
/root
[redhat@openEuler ~]$ exit
logout
[root@openEuler ~]# bash
[root@openEuler ~]# echo $MyPath
/root
[root@openEuler ~]# exit
exit
4.4 删除变量
当变量不再使用时,我们就需要把它删除,从而可以节省内存空间。
要删除变量就需要使用 unset 命令。它的语法为:
unset 变量名
使用示例:我们删除之前创建的 name 变量。
[root@openEuler ~]# echo $name
lisi
[root@openEuler ~]# unset name
[root@openEuler ~]# echo $name
[root@openEuler ~]#
面试题:如下代码输出的结果是什么?
[root@openEuler ~]# cat test.sh
user1=`whoami`
[root@openEuler ~]# sh test.sh
[root@openEuler ~]# echo $user1
A. 当前用户
B. root
C. 空
D. 报错
4.5 变量的类型
Shell 是一种动态类型语言,也是一种弱类型的语言。所以在定义变量时,我们无须指定具体存放的值是什么类型(如:整数、小数、字符串等)。
但是在 Shell,变量也存在类型,它有以下几种类型:
- 自定义变量
- 环境变量
- 只读变量
- 位置变量
- 预定义变量
4.5.1 自定义变量
在 Shell 中,通常情况下都是用户在使用时直接定义的变量,而无须先进行定义。
1)自定义变量的格式:
变量名=变量值
注意:
- 等号两边不能有空格
- 变量值如果是数字一般不加引号,如果是字符串推荐加引号;如果值包含有特殊字符或空格就必须要有引号
- 引号可以是以下几种:
- 单引号:被单引号包含的内容会原样输出
- 双引号:被双引号包含的内容会把变量的值替换后再输出
- 反引号:被反引号包含的命令执行的结果赋值给变量
2)查看变量的值
在 Shell 中查看变量的值有以下几种:
- echo $变量名
- echo ${变量名}
- set 查看系统中所有变量
- env 来查看系统环境变量
- declare 来查看输出的所有变量、函数等。
使用示例:
[root@openEuler ~]# a=1
[root@openEuler ~]# echo $a
1
[root@openEuler ~]# echo ${a}
1
[root@openEuler ~]# ts=cs1
[root@openEuler ~]# echo $ts
cs1
[root@openEuler ~]# b = "hello"
-bash: b: command not found
[root@openEuler ~]# b ="hello"
-bash: b: command not found
[root@openEuler ~]# b= "hello"
-bash: hello: command not found
[root@openEuler ~]# b="hello"
[root@openEuler ~]# b='world'
[root@openEuler ~]# echo $b
world
[root@openEuler ~]# b=`pwd`
[root@openEuler ~]# echo $b
/root
[root@openEuler ~]#
[root@openEuler ~]# env
SHELL=/bin/bash
HISTCONTROL=ignoredups
HISTSIZE=1000
HOSTNAME=openEuler
MyPath=/root
PWD=/root
LOGNAME=root
MOTD_SHOWN=pam
HOME=/root
LANG=en_US.UTF-8
对于单引号和双引号的区别:
[root@openEuler ~]# age=18
[root@openEuler ~]# name=list
[root@openEuler ~]# echo "$name,$age"
list,18
[root@openEuler ~]# echo '$name,$age'
$name,$age
[root@openEuler ~]# echo "\$name"
$name
3)使用declare来定义变量
格式如下:
declare attribute variable
attribute 它可以是 +/- 来定义,如果是 - 表示为这个属性设置值,如果是 + 表示取消此属性
有以下属性:
-p:表示所有变量的值
-i:将变量定义为整数,如果不能转换为整数则为 0
-r:将变量声明为只读变量
-a:将变量声明为数组
使用示例:
# 使用 -i 属性来声明定义的变量 n 的类型是一个整数,如果n的值不能转换为整数,则以0填充
[root@openEuler ~]# declare -i n
[root@openEuler ~]# n=5
[root@openEuler ~]# n=a
[root@openEuler ~]# echo $n
0
[root@openEuler ~]# n=4
[root@openEuler ~]# echo $n
4
# 使用 -r 属性来声明变量为只读的变量,对于只读变量来说,只能赋值一次,即它的值设置好后,就不能发生变量。
[root@openEuler ~]# declare -r name
[root@openEuler ~]# name=wangwu
-bash: name: readonly variable
[root@openEuler ~]# echo $name
list
[root@openEuler ~]# x=3
[root@openEuler ~]# declare -r x
[root@openEuler ~]# x=5
-bash: x: readonly variable
# 使用 -a 属性来声明变量是一个数组,数组是通过下标来设置或获取数据的,下标是从0开始,最大的下标是数组的长度 - 1。如果希望获取数组中所有的元素(值),可以使用 `数组名[@]` 的形式来获取
[root@openEuler ~]# declare -a arr
[root@openEuler ~]# arr[0]="hello"
[root@openEuler ~]# arr[1]="world"
[root@openEuler ~]# arr[2]="shell"
[root@openEuler ~]# echo $arr[0]
hello[0]
[root@openEuler ~]# echo ${arr[0]}
hello
[root@openEuler ~]# echo ${arr[2]}
shell
[root@openEuler ~]# echo ${arr[5]}
[root@openEuler ~]# echo ${arr[@]}
hello world shell
4)通过交互定义变量
在编写 Shell 脚本时,我们会用到与用户进行交互,将用户输入的值赋予我们定义的变量。此时我们可以使用 read 命令来实现。它的使用格式如下:
read -p "输入提示信息" 变量名称
使用示例:
[root@openEuler ~]# read -p 'please input your number:' num
please input your number:18
[root@openEuler ~]# echo $num
18
[root@openEuler ~]#
除了这种在命令行中使用外,还可以在脚本中使用。格式如下:
[root@openEuler ~]# vim my_read.sh
[root@openEuler ~]# cat my_read.sh
#!/bin/bash
echo 'please input your number:'
read num
echo $num
[root@openEuler ~]# ll
total 40
-rw-------. 1 root root 881 Mar 16 20:10 anaconda-ks.cfg
drwxr-xr-x. 2 root root 4096 Mar 17 10:29 d1
-rwxr-xr-x. 1 root root 24 Mar 23 14:32 for_test.sh
-rw-r--r--. 1 root root 49 Mar 16 21:07 hehe.txt
-rw-r--r--. 1 root root 35 Mar 17 09:51 message.txt
-rw-r--r--. 1 root root 69 Mar 23 16:12 my_read.sh
-rwxr-xr-x. 1 root root 26 Mar 17 11:18 my.sh
-rw-r--r--. 1 root root 183 Mar 16 21:03 passwd
-rw-r--r--. 1 root root 34 Mar 23 15:33 test.sh
-rw-r--r--. 1 root root 54 Mar 17 10:21 test.txt
[root@openEuler ~]# bash my_read.sh
please input your number:
20
20
4.5.2 环境变量
在 Shell 中环境变量分为系统环境变量和用户环境变量。系统变量的定义参数前变量范围中的内容。而用户环境变量我们可以用户的家目录下的 ~/.bash_profile
文件或 ~/.bashrc
文件中定义的变量。
在 Linux 中给我们内置了很多的环境变量,如:
# 执行命令时搜索的路径,以冒号进行分隔
[root@openEuler ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/
# 当前登录的用户名
[root@openEuler ~]# echo $USER
root
# 当前登录用户的家目录
[root@openEuler ~]# echo $HOME
/root
# 定义在命令模式下可以使用的命令行的长度
[root@openEuler ~]# echo $COLUMNS
156
# 命令存放的历史文件
[root@openEuler ~]# echo $HISTFILE
/root/.bash_history
其实,以上的这些系统变量我们可以使用 env 命令来查看:
[root@openEuler ~]# env
SHELL=/bin/bash
HISTCONTROL=ignoredups
HISTSIZE=1000
HOSTNAME=openEuler
MyPath=/root
PWD=/root
LOGNAME=root
MOTD_SHOWN=pam
HOME=/root
LANG=en_US.UTF-8
user1=root
SSH_CONNECTION=192.168.72.1 8175 192.168.72.150 22
SELINUX_ROLE_REQUESTED=
TERM=xterm
USER=root
SELINUX_USE_CURRENT_RANGE=
SHLVL=1
SSH_CLIENT=192.168.72.1 8175 22
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
SELINUX_LEVEL_REQUESTED=
MAIL=/var/spool/mail/root
SSH_TTY=/dev/pts/0
_=/usr/bin/env
4.5.3 只读变量
如果希望定义的变量在程序的执行过程中不会发生变量,此时就可以声明为只读变量。声明只读变量有两种方式:
- declare -r 变量名称
- readonly 变量名称
使用示例:
[root@openEuler ~]# n=1
[root@openEuler ~]# n=2
[root@openEuler ~]# readonly n
[root@openEuler ~]# n=3
-bash: n: readonly variable
当然,除我们先声明变量再变为只读外,也可以在声明的时候就定义为只读。
[root@openEuler ~]# readonly m=5
[root@openEuler ~]# m=6
-bash: m: readonly variable
[root@openEuler ~]# echo $m
5
对于只读变量来说,不能使用 unset 来删除。
4.5.4 位置变量
在很情况下,Shell 脚本执行时是需要接收用户输入的参数的,这时就需要使用到位置变量。
1)特殊变量
$#:表示参数的个数
$n:其中n表示大于0的数字,表示第 n 个参数
$0:表示这个被执行的脚本名称
$*:表示获取所有的参数列表
$@:表示获取所有的参数列表
使用示例:
# 编写一个脚本
[root@openEuler ~]# vim t1.sh
# 查看脚本内容
[root@openEuler ~]# cat t1.sh
#!/bin/bash
echo "total parameters: $#"
echo "first parameter: $1"
echo "second parameter: $2"
echo "filename is: $0"
echo "all arguments: $*"
echo "all arguments: $@"
# 运行脚本
[root@openEuler ~]# bash t1.sh python shell golang html
total parameters: 4
first parameter: python
second parameter: shell
filename is: t1.sh
all arguments: python shell golang html
all arguments: python shell golang html
2)$*
和$@
的区别
[root@openEuler ~]# set -- "hello" "world" "shell"
[root@openEuler ~]# echo $*
hello world shell
[root@openEuler ~]# echo $@
hello world shell
[root@openEuler ~]# for fname in "$@"; do echo $fname; done
hello
world
shell
[root@openEuler ~]# for fname in "$*"; do echo $fname; done
hello world shell
从上面的输出结果可以发现 $*
和$@
的异同如下:
$*
和$@
都可以获取到所有的参数列表,如果不加引号,它们没区别,如果加了引号区别如下:$@
它是将参数以 “参数1” “参数2” 获取的,而$*
则是将参数以"参数1 参数2"的方式获取的
3)$?
执行结果状态
当脚本命令执行后,会有一个执行后的状态值,如果这个值为 0 则表示前一条命令执行成功,否则表示执行不成功,即有错误。
[root@openEuler ~]# cat t2.sh
cat: t2.sh: No such file or directory
[root@openEuler ~]# echo $?
1
[root@openEuler ~]# cat t1.sh
#!/bin/bash
echo "total parameters $#"
echo "first parameter $1"
echo "second parameter $2"
echo "filename is $0"
echo "all arguments $*"
echo "all arguments $@"
[root@openEuler ~]# echo $?
0
4)特殊状态变量
在 Shell 特殊状态变量有以下几个:
$$:返回脚本进程的PID
$!:获取上一个在后台的工作进程的PID
$_:保存的是之前所执行命令的最后一个参数
以上三个都不常用,大家了解即可。
[root@openEuler ~]# vim t2.sh
[root@openEuler ~]# cat t2.sh
#!/bin/bash
[ $# -ne 2 ] && {
echo "must be two arguments"
exit 3
}
echo ok
echo "current shell pid: $$" # 输出当前脚本执行时产生的 PID 值
nohup ping www.baidu.com & 1>/dev/null # 将 ping 命令的执行放到后台运行,并且不输出任何信息
echo $! # 输出上一个后台运行的命令的 PID 值
将脚本编写好后,通过如下的方式来执行,从而得到如下的结果。
[root@openEuler ~]# bash t2.sh
must be two arguments
[root@openEuler ~]# bash t2.sh a b
ok
current shell pid: 2540 # 脚本运行的PID
2541 # ping命令运行的PID
nohup: appending output to 'nohup.out'
[root@openEuler ~]# ps -ef | grep ping # 查看ping 进程
root 2541 1 0 17:10 pts/0 00:00:00 ping www.baidu.com
root 2543 1666 0 17:10 pts/0 00:00:00 grep --color=auto ping
[root@openEuler ~]# echo $_ # 输出上一次命令的最后一个参数
b
4.6 变量的特殊用法
在实际工作中,对变量的值还会有一些特殊的用法,例如对变量的值里德截取、替换、删除等。
要想实现这些功能,我们需要使用如下的格式:
${变量} 获取变量的值
${#变量} 返回变量的长度,即字符串的长度
${变量:start} 返回变量从start开始截取到变量最后,它是从 0 开始
${变量:start:length} 截取变量的值中从 start 开始,到 length 长度的内容
${变量#word} 将变量开头删除最短匹配的 word 子串
${变量##word} 将变量开头删除最长匹配的 word 子串
${变量%word} 从变量结尾删除最短的 word
${变量%%word} 从变量结尾删除最长的 word
${变量/parttern/string} 使用string代替第一个匹配的 parttern
${变量//parttern/string} 使用string代替所有匹配的 parttern
4.6.1 获取变量
${变量} 获取变量的值
${
#变量} 返回变量的长度,即字符串的长度
使用示例:
[root@openEuler ~]# name="I am openEuler"
[root@openEuler ~]# echo ${name}
I am openEuler
[root@openEuler ~]# echo ${#name}
14
4.6.2 变量值截取
${变量:start} 返回变量从start开始截取到变量最后,它是从 0 开始
${变量:start:length} 截取变量的值中从 start 开始,到 length 长度的内容
使用示例:
[root@openEuler ~]# name="I am openEuler"
[root@openEuler ~]# echo ${name:2}
am openEuler
[root@openEuler ~]# echo ${name:2:5}
am op
[root@openEuler ~]#
4.6.3 字符串删除
${变量#word} 将变量开头删除最短匹配的 word 子串
${变量##word} 将变量开头删除最长匹配的 word 子串
${变量%word} 从变量结尾删除最短的 word
${变量%%word} 从变量结尾删除最长的 word
使用示例:
[root@openEuler ~]# name="abcABC123ABCabc"
[root@openEuler ~]# echo ${name}
abcABC123ABCabc
[root@openEuler ~]# echo ${name#a*c} # 匹配最短,它是从前往后删除
ABC123ABCabc
[root@openEuler ~]# echo ${name##a*c} # 匹配最长,它是从前往后删除
[root@openEuler ~]# unset name
[root@openEuler ~]# name="abcABC123ABCabc"
[root@openEuler ~]# echo ${name}
abcABC123ABCabc
[root@openEuler ~]# echo ${name%a*c} # 匹配最短,从后往前删除
abcABC123ABC
[root@openE