Bash shell基础
一 介绍
类比:
-
shell语法《==============》python语法
-
bash解释器《============》python解释器
-
平台 《=================》平台
shell是一门解释型、弱类型、动态语言
二、交互式与非交互式(接收指令形式的不同)
shell环境接收指令的方式与两种:
1、交互式:
(1)每次接收的是单独一条命令
(2)交互的含义是你给它一条指令它立即给你返回结果
它就好比是你去路边摊吃大餐: 老板问:吃什么 你:吃蒸羊羔、蒸鹿茸、蒸熊掌 老板问:怎么个做法 你:羊羔烤七分熟、鹿茸清蒸、熊掌红烧 老板问:加不加香菜 你:多加点香菜
2、非交互式:
(1)每次接收的一个文件,文件里放置了很多条命令,运行时shell会自上而下依次运行
(2)非交互式的含义是,将实现某一需求的一系列指令堆砌到一起,扔到一个文件中,形成一个简单的程序然后交给shell执行,
还是去路边摊吃大餐的例子 1、你事先将将实现你需求的指令都写到了一个文件中 食材:羊羔、鹿茸、熊掌 烤羊羔 清蒸鹿茸 红烧熊站 多加香菜 2、然后你你跑到大排档门口,话都不说,直接把文件丢给了老板 老板就照着文件里的指令依次开始做
这种单文件里堆砌命令完成一个系统工作的程序,我们称之为脚本程序
交互式
[root@web01 ~]# sh # 运行bash或sh命令,进入shell交互式环境, sh-4.2# echo 123 123 sh-4.2# echo 666 666 sh-4.2# echo 777 777 sh-4.2# exit exit [root@web01 ~]# [root@web01 ~]# [root@web01 ~]# python # 进入python交互式环境, Python 2.7.5 (default, Oct 14 2020, 14:45:30) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print("123") 123 >>> print("666") 666 >>> print("777") 777 >>> exit() [root@web01 ~]#
非交互式
把一条条的指令全部都放入一个文件中,然后用解释器解释执行 bash a.sh python a.py
区别:
1、交互式是运行一条命令拿到一条结果,做的事情比较单一,用于日常管理
非交互式是把一堆命令放到了一个文件中,是一堆命令堆砌而成的一个系统性工作,比如打包备份上传到远程备份机,需要多条命令堆砌起来完成这个需求。
2、内置环境变量PS1在交互式环境下有值,在非交互式环境下为空/布尔值为假
你可以在交互式环境或脚本文件中echo $PS1来验证,这可以作为代码中识别当前环境是否为交互式环境的一种手段
交互式下 [root@web01 ~]# echo $PS1 \[\e[37;40m\][\[\e[32;1m\]\u\[\e[37xxxxxxxxxxx [root@web01 ~]# [ "$PS1" ] [root@web01 ~]# echo $? 0 非交互式下 [root@web01 ~]# cat a.sh echo $PS1 [ "$PS1" ] echo $? [root@web01 ~]# bash a.sh 1
三 变量
登录用户即进入交互式环境,与python的交互式环境都是一回事
[root@localhost ~]# x=1 [root@localhost ~]# x=2 [root@localhost ~]# echo $x 2 [root@localhost ~]# name1=egon [root@localhost ~]# echo $name1 egon [root@localhost ~]# echo ${name1} egon [root@localhost ~]# unset name1 [root@localhost ~]# echo $name1 [root@localhost ~]# [root@localhost ~]# domain=www.baidu.com [root@localhost ~]# ping -c1 $domain [root@localhost ~]# name=egon [root@localhost ~]# echo "hello $name" hello egon
应用示例
[root@localhost ~]# vim heartbeat.sh [root@localhost ~]# cat heartbeat.sh #!/bin/bash ip=192.168.11.20 ping -c1 $ip &>/dev/null if [ $? = 0 ];then echo "host $ip is alive" else echo "host $ip is down!!!" fi [root@localhost ~]# chmod +x heartbeat.sh [root@localhost ~]# ./heartbeat.sh host 192.168.11.20 is down!!!
四 引号对变量的影响
双引号=》弱引用
[root@localhost ~]# name=egon [root@localhost ~]# echo "hello $name"
双引号=》强引用
[root@localhost ~]# echo 'hello $name' hello $name
反引号=》取结果
[root@localhost ~]# today=`date +%F` [root@localhost ~]# echo $today 2020-08-11 [root@localhost ~]# today=$(date +%H:%M:%S) [root@localhost ~]# echo $today 22:05:55 示例 [root@localhost ~]# tar czf `date +%F`_bak.tar.gz /tmp
变量值包含空格时,需要加上双引号包含
[root@localhost ~]# msg="hello egon" [root@localhost ~]# echo $msg hello egon
五 变量作用域
环境变量: 在当前shell及子shell生效!
自定义变量: 仅在当前shell生效!
[root@localhost ~]# x=1 [root@localhost ~]# export x [root@localhost ~]# bash [root@localhost ~]# echo $x 1
set 查看所有变量(包括自定变量和环境变量)
env 查看环境变量
环境变量的初始化源自于5种配置文件(下一小节会详细介绍):
/etc/profile /etc/profile.d/*.sh /etc/bashrc ~/.bash_profile ~/.bashrc
系统自带环境变量
[root@localhost ~]# echo $PS1 [\u@\h \W]\$ [root@localhost ~]# echo $HOSTNAME localhost.localdomain [root@localhost ~]# echo $USER root [root@localhost ~]# echo $UID 0 [root@localhost ~]# echo $SHELL /bin/bash [root@localhost ~]# echo $HISTSIZE 5 [root@localhost ~]# echo $MAIL /var/spool/mail/root [root@localhost ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
六 登录shell与非登录shell(加载配置的不同)
1、按照shell接收指令的形式上的不同,我们之前把shell分为交互式与非交互式
2、按照进入shell环境的方式的不同,我们可以把shell分为登录式与非登录式
# 1、登陆式shell(日常最多): 用户在登录时启动的shell,例如终端模拟器,如ssh,或者在控制台直接输入的用户名和密码。 # 2、非登陆式shell 登录后被用户手动启动的shell,例如在终端中直接输入bash或sh命令。
交互式与非交互式的核心区别就是接收指令的形式上的不同,而登录式与非登录的核心区别表面是进入shell环境的形式上的不同,但实际上的区别是读取与执行的配置文件的不同
解释
但凡进入到某一个shell环境中,本质上都是启动了一个shell进程, 每个进程启动时都必须以某个用户身份启动以标识作为其在系统中识别权限的依据, 而linux系统中一切皆为文件,一个用户是由一系列文件构成的 所以,当你你以不同的方式进入shell环境中时,操作系统如何区分? 答:就是通过加载不同配置文件来区分的,这些配置文件用于初始化或保存当前shell的工作环境
shell环境相关配置文件就以下几种
# 1、系统级配置 /etc/profile /etc/profile.d/*.sh /etc/bashrc # 2、用户级配置 ~/.bash_profile ~/.bashrc # 补充说明: 1、profile类文件, 设定环境变量, 登陆前运行的脚本和命令。 2、bashrc类文件, 设定本地变量, 定义命令别名 3、如果全局配置和个人配置产生冲突,以个人配置为准。
影响bash shell的文件(/etc下的是系统级的文件,家目录下的是用户级)—》centos7.9
当用户通过用户名和密码登录系统或者su - egon时,系统会启动一个login Shell,依次加载如下配置 1. /etc/profile # 内部会调用/etc/profile.d下的脚本,如果是centos9.3则还会多调用一次/etc/bashrc 2. ~/.bash_profile 3. ~/.bashrc # 内部再调用/etc/bashrc
在非登录shell即su egon时
non-login shell依次加载如下配置 1、~/.bashrc # 内会依次调用/etc/bashrc、/etc/profile.d/*.sh 了解知识:看代码解析调用顺序 ~/.bashrc 内部会调用 /etc/bashrc ,而/etc/bashrc内部会调用/etc/profile.d/下的脚本 代码解析如下 可以看一下:/etc/bashrc的文件中的第54行增加了如下代码 if ! shopt -q login_shell满足当前shell不等于login_shell,即非登录shell时 会执行到第75行的for循环执行/etc/profile.d/*.sh 也就是说在进入非登录式shell时,除了执行 ~/.bashrc /etc/basrh 还会执行:/etc/profile.d/*.sh
强调:
你必须避免在上述文件中添加任何会产生输出的命令(例如echo命令),否则会干扰scp或者其他依赖ssh的命令的执行,还有后续要学的rsync命令。原理我们将在后续章节学习到
如果你确实需要在bashrc文件中添加echo或类似的命令, 一个比较好的做法可能是检查当前会话是否为一个交互式会话, 只在交互式会话中执行这些命令。你可以参考以下的bash语句: if [ "$PS1" ]; then echo "This is an interactive session." fi 在这个例子中,$PS1环境变量通常在交互式bash会话中被设置, 所以你可以使用它来检测当前会话是否为交互式。 类似的,你可以把你的echo命令放在这样的条件判断语句中, 以避免在非交互式会话(比如scp和ssh命令)中执行它们。
Ps:
1、vim /home/user1/.bash_history -------每次登陆用户的操作都记录其中 2、su - user1进行操作然后退出,查看该文件内容 3、vim /home/user1/.bash_logout --------添加一条 4、>/home/user1/.bash_history这样每次退出用户后都会自动清空~/.bash_history中的内容
七 元字符
shell语法中的特殊字符
-
1、“ 与$():取命令的结果
[root@localhost ~]# echo pwd
/root [root@localhost ~]# echo $(pwd) /root 不一样的地方在于$()可以嵌套,而``不能嵌套 [root@localhost ~]# echo $(ls $(pwd)) -
2、~家目录
-
3、.与..
-
4、!取反
[root@localhost ~]# touch /test/{1.txt,2.txt,a.txt,aaa_bbb.txt} [root@localhost ~]# find /test ! -name 1.txt /test /test/2.txt /test/a.txt /test/aaa_bbb.txt [root@localhost ~]# ls /test/[!0-9].txt # .txt前只有一个字符,但是非数字 /test/a.txt [root@localhost ~]# ls /test/[^0-9].txt # .txt前只有一个字符,但是非数字 /test/a.txt -
5、@无特殊意义
-
6、#注释
-
4、$取变量值
[root@localhost ~]# x=1 [root@localhost ~]# echo $x 1 -
5、%、-、+运算符
# 数学运算 # 1、bc是比较常用的linux计算工具了,而且支持浮点运算: [root@localhost ~]# res= echo 1+1 | bc
[root@localhost ~]# echo $res 2 [root@localhost ~]# res= echo 10 % 3 | bc
[root@localhost ~]# echo $res 1 [root@localhost ~]# res= echo 1.2+1.3|bc
[root@localhost ~]# echo $res 2.5 [root@localhost ~]# res= echo 5.0+3.0|bc
[root@localhost ~]# echo $res 8.0 [root@localhost ~]# res= echo "scale=2;5.0/3.0"|bc
[root@localhost ~]# echo $res 1.66 [root@localhost ~]# res= echo "scale=2;5.0/6.0"|bc
[root@localhost ~]# echo $res .83 # 2、expr不支持浮点数计算。而且要注意数字与运算符中的空格 [root@localhost ~]# res= expr 5 / 3
# 不支持浮点计算[root@localhost ~]# echo $res 1 [root@localhost ~]# res= expr 1+1
# 注意:要有空格[root@localhost ~]# echo $res 1+1 [root@localhost ~]# res= expr 1 + 1
[root@localhost ~]# echo $res 2 # 3、$(()) 同expr,不支持浮点数运算 [root@localhost ~]# echo $((1+1)) 2 [root@localhost ~]# echo $((1.0+2.0)) -bash: 1.0+2.0: 语法错误: 无效的算术运算符 (错误符号是 ".0+2.0") #4、let 不支持浮点数运算,而且不支持直接输出,只能赋值 [root@localhost ~]# let res=1+1 [root@localhost ~]# echo $res 2 [root@localhost ~]# [root@localhost ~]# let res=50/5 [root@localhost ~]# echo $res 10 [root@localhost ~]# let c=1.3*3 -bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3" -
6、^同!一样
-
7、*任意多个字符
[root@localhost ~]# touch 1.txt 2.txt aa.txt aaa.txt [root@localhost ~]# rm -rf *.txt [root@localhost ~]# touch 1.txt 2.txt aa.txt aaa.txt a1c.txt [root@localhost ~]# ls *.txt 1.txt 2.txt a1c.txt aaa.txt aa.txt -
8、()在子shell中执行
[root@localhost ~]# (x=1) [root@localhost ~]# echo $x 应用 [root@localhost ~]# (umask 066;touch a.txt) # umask的设置只在子shell中有效 [root@localhost ~]# ll a.txt -rw-------. 1 root root 0 8月 13 15:22 a.txt [root@localhost ~]# touch b.txt [root@localhost ~]# ll b.txt -rw-r--r--. 1 root root 0 8月 13 15:23 b.txt -
9、_下划线:无特殊意义,可以用于名字的声明
-
10、=赋值,判断相等性
[root@localhost ~]# [ 1 = 1 ] # 条件1 = 1的左右两边必须有空格 [root@localhost ~]# echo $? # 判断上一条命令的结果是否为真,0=》true 0 -
11、|管道:把一个进程的处理结果传递给另外一个进程
[root@localhost ~]# ps aux | grep python xargs参数传递,把上一个命令的结果作为下一个命令的参数 [root@localhost ~]# find /home/ -type d -name "test*" |xargs ls 1.txt 2.txt 3.txt [root@localhost ~]# ls /home/test 1.txt 2.txt 3.txt -
12、\转义特殊字符
[root@localhost ~]# mkdir a\ b.txt # 虽然可以,但不推荐 [root@localhost ~]# ll 总用量 0 drwxr-xr-x. 2 root root 6 8月 13 15:35 a b.txt [root@localhost ~]# echo $RMB # 默认会当成变量 [root@localhost ~]# echo '$RMB' # 取消特殊意义 $RMB [root@localhost ~]# echo \$RMB # 取消特殊意义 $RMB -
13、[]条件测试,后续会详细介绍
[root@localhost ~]# name="egon" [root@localhost ~]# [ $name = "egon" ];echo $? 0 [root@localhost ~]# name="adf" [root@localhost ~]# [ $name = "egon" ];echo $? 1 [root@localhost ~]# [ -d /test ];echo $? -
14、引号
'' 强引用(在单引号中都视为普通字符) " " 弱引用 (在双引号中保留变量) [root@localhost ~]# x=111 [root@localhost ~]# echo "$x" 111 [root@localhost ~]# echo '$x' $x [roo -
15、;与&&与||连接多条命令
[root@localhost home]# gagaga;ls # 不论前一条命令运行成功与否,都会执行后续命令 bash: gagaga: 未找到命令... egon [root@localhost home]# gagaga && ls # 只有前一条命令执行成功,才会执行后续命令 bash: gagaga: 未找到命令... [root@localhost home]# ls /test || mkdir /test # 前一条命令执行不成功才会执行后续命令 0.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt -
16、/路径分隔符
-
17、{}包含
[root@localhost home]# touch /test/{0..9}.txt [root@localhost home]# ls /test/ 0.txt 1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt -
18、&后台运行
[root@localhost home]# echo "hello";sleep 3;echo "world" & -
19、重定向
> >> 输出重定向 < << 输入重定向 > 覆盖 >> 追加 [root@localhost home]# cat >> a.txt << EOF > 111 > 222 > 333 > EOF 0标准输入、1标准正确输出、2标准错误输出,&标准正确和错误输出 [root@localhost home]# pwd 1>a.txt [root@localhost home]# cat a.txt /home [root@localhost home]# gagag 2>a.txt [root@localhost home]# cat a.txt bash: gagag: 未找到命令... [root@localhost home]# gagaga &>/dev/null < << 输入重定向 [root@localhost ~]# mysql -uroot -p123 < bbs.sql [root@localhost home]# grep root < /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [root@localhost home]# dd if=/dev/zero of=/a.txt bs=1M count=10 记录了10+0 的读入 记录了10+0 的写出 10485760字节(10 MB)已复制,0.024387 秒,430 MB/秒 [root@localhost home]# dd /b.txt bs=1M count=10 记录了10+0 的读入 记录了10+0 的写出 10485760字节(10 MB)已复制,0.0202365 秒,518 MB/秒 [root@localhost home]# ll /a.txt -rw-r--r--. 1 root root 10485760 8月 13 16:02 /a.txt [root@localhost home]# ll /b.txt -rw-r--r--. 1 root root 10485760 8月 13 16:03 /b.txt -
20、?任意一个字符
[root@localhost ~]# ls ??.txt aa.txt [root@localhost ~]# ls a?c.txt a1c.txt [root@localhost ~]# rm -rf *.txt -
21、范围中的任意一个字符 [12] [ac] [a-z] [0-9]
[root@localhost ~]# touch a1c a2c axc aXc axd [root@localhost ~]# ls a?c a1c a2c axc aXc [root@localhost ~]# ls a[1x]c a1c axc [root@localhost ~]# ls a[a-z]c axc aXc [root@localhost ~]# ls a[A-Z]c # 不区分大小写 axc aXc [root@localhost ~]# ls a[x]c axc [root@localhost ~]# ls a[X]c aXc [root@localhost ~]# ls a[0-9]c a1c a2c [root@localhost ~]# ls /dev/sd[a-z]* /dev/sda /dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb1
八 Bash SHELL基础
例1
[root@localhost ~]# vim if.sh [root@localhost ~]# cat if.sh #!/usr/bin/env bash domain="www.baidu.com" ping -c2 $domain &>/dev/null if [ $? = 0 ];then echo "network is ok" else echo "network is down!!!" fi [root@localhost ~]# chmod +x if.sh [root@localhost ~]# ./if.sh network is ok
例2
[root@localhost ~]# vim for.sh [root@localhost ~]# ll for.sh -rw-r--r--. 1 root root 61 8月 13 16:17 for.sh [root@localhost ~]# . for.sh egon tom jack
例3
[root@localhost ~]# vim for1.sh [root@localhost ~]# cat for1.sh #!/bin/bash for i in {1..5} do echo $i done [root@localhost ~]# source for1.sh 1 2 3 4 5
例4
[root@localhost ~]# vim for2.sh [root@localhost ~]# cat for2.sh #!/bin/bash for i in {1..10} do ip=192.168.12.$i ping -c1 $ip &>/dev/null if [ $? = 0 ];then echo "$i is up" else echo "$i is down!!!" fi done [root@localhost ~]# vim for2.sh [root@localhost ~]# source for2.sh 192.168.12.1 is up 192.168.12.2 is down!!! 192.168.12.3 is up 192.168.12.4 is down!!! ......
练习
1、编写脚本创建100个用户并设置好密码,用户名为user1,user2,user3... 提示非交互式修改密码: echo 123 |passwd user1 --stdin