Shell编程bash原理及特性 元字符详解

一. shell介绍

1. 什么是shell?

Shell 是一类应用程序的统称,该类应用程序负责连接用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核来控制计算机硬件。
具体来说,linux系统上自带多种shell程序.

[root@localhost ~]# chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

通常用户登录成功后执行的shell程序为:/bin/bash

[root@localhost ~]# head -1 /etc/passwd
root:x:0:0:root:/root:/bin/bash

其实,shell程序例如bash的本质就是一个解释器,shell本身就是一门解释型、弱类型、动态语言,与python相对应,Python属于解释型、强类型、动态语言,我们平时登录成功一个用户后进入的就是bash解释器的交互式环境,我们敲的命令其实都属于shell这门语言的语法.


编程语言分类
​ 机器语言:直接使用二进制指令去编写程序

​ 问题:

​ 1、二进制指令难以记忆

​ 2、开发复杂:实现一个简单的操作都需要很多条指令才能完成

​ 汇编语言:用英文标签替代机器语言的二进制指令编写程序

​ 解决的问题:不用记忆二进制指令

​ 存在的问题:开发仍然比较复杂

​ 高级语言:用人类能理解的表达方式去编写程序

​ 1、编译型(C语言,go语言):

​ 2、解释型(shell、python):

​ 执行效率:机器语言> 汇编语言>高级语言(编译型>解释型)

​ 开发效率:机器语言<汇编语言<高级语言


计算机的三层结构(应用程序、操作系统、计算机硬件)底层与封装


## 什么是编程语言?
编程语言的本质就是一门语言,而语言是用来一种事物和另外一种事物沟通的介质

人=============》编程语言=============》计算机


##什么是编程
分为两个环节
    1、想清楚做事的步骤
    2、用一种计算机能听懂的语言(编程语言)

## 为何要编程?
为了让计算机帮人去做事,从而解放人力

## 什么是程序
程序就是一堆代码文件

## 什么是进程
进程是程序的运行过程,也可以说是操作系统干活的过程,因为是操作系统负责控制硬件来运行应用程序

ps:进程与进程之间的内存空间是互相隔离的

## 计算机体系的三层结构

应用程序
操作系统
计算机硬件

## 什么是shell
shell是一门编程语言,用来与计算机沟通,从而控制计算机的

## 什么是shell解释器
用于解释执行shell语言语法/命令的一个应用软件
[root@Centos7 ~]# chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

## 什么是shell script
用shell这门编程语言编写的程序

## 运行shell程序两种方式
方式一:交互式环境(登录用户之后默认就会进入用户的shell解释器交互式环境)
    优点:输入命令立即拿到代码的运行结果,即时运行
    缺点:无法永久保存代码,退出交互式环境,代码全部丢失
方式二:把代码写到文件中,然后运行???
    优点:可以永久保存代码,以便后期重复使用
    缺点:无法即时运行

2. 为何要用shell

那还用说吗,一些日常的运维工作自动化、配合计划任务威力无穷,彻底解放双手,你说它不香吗?

例如:

拉取最新代码(git pull)
编译打包
上传并部署到远程服务器

3. Shell VS python

shell语言

Shell 脚本的优势在于处理偏操作系统底层的业务,例如,Linux 内部的很多应用(有的是应用的一部分)
都是使用 Shell 脚本开发的,因为有 1000 多个 Linux 系统命令为它作支撑,特别是 Linux 正则表达
式以及三剑客 grep、awk、sed 等命令。
对于一些常见的系统脚本,使用 Shell 开发会更简单、更快速,例如,让软件一键自动化安装、优化,监控
报警脚本,软件启动脚本,日志分析脚本等,虽然 Python 也能做到这些,但是考虑到掌握难度、开发效
率、开发习惯等因素,它们可能就不如 Shell 脚本流行以及有优势了。对于一些常见的业务应用,使用
Shell 更符合 Linux 运维简单、易用、高效的三大原则。

python语言

Python 是近几年非常流行的语言,它不但可以用于脚本程序开发,也可以实现 Web 程序开发(知乎、豆
瓣、YouTube、Instagram 都是用 Python 开发),甚至还可以实现软件的开发(大名鼎鼎的
OpenStack、SaltStack 都是 Python 语言开发)、游戏开发、大数据开发、移动端开发。
现在越来越多的公司要求运维人员会 Python 自动化开发,Python 也成了运维人员必备的技能,每一个运
维人员在熟悉了 Shell 之后,都应该再学习 Python 语言。
Python 语言的优势在于开发复杂的运维软件、Web 页面的管理工具和 Web 业务的开发(例如 CMDB 自动
化运维平台、跳板机、批量管理软件 SaltStack、云计算OpenStack 软件)等。

二. bash解释器特性

启动操作系统后,linux系统会默认提供5个操作终端(multics=》unix=》。。。=》linux),可以用Ctrl + Alt + Fn(n=1,2,3,4,5...) 快捷键切换,我们进入任意终端后输入账号密码登录用户后,就
会执行该用户在/etc/passwd文件中指定的解释器程序,然后进入解释器的交互式环境.

[root@localhost ~]# head -1 /etc/passwd
root:x:0:0:root:/root:/bin/bash  # 最后一个字段就是指定的解释器程序

何为交互式环境???

1、我们为解释器输入指令,解释器解释执行(调用操作系统接口执行)然后后给我们返回结果,这就叫交互
2、进入解释器交互式环境后普通用户的提示符是$,管理用户是#
3、shell命令的基本格式/语法格式:命令 选项 参数

命令优先级从高到低

==> alias
 ==> Compound Commands
  ==> function
   ==> build_in
    ==> hash
     ==> PATH
      ==> error: command not found
       
       
#1、alias别名
可以用alias查看
可以用命令来制作:alias 别名="命令 选项 参数"

#2、Compound Commands复合命令
复合命令指的是能够将一组命令封装到一个代码块里的命令,例如if、for、while等

#3、function函数

#4、build in内建命令
可以用type查看,或者用man builtin
[root@egon ~]# type cd
cd 是 shell 内嵌

#5、hash
当别名和内部命令都搜到不到命令时,会检索PATH中的路径

[root@egon ~]# sed -n 's/:/\n/gp' <<< PATH
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin
但是PATH中包含的路径太多了,而且每个路径中的可执行文件也很多
如果每次都要搜索每个路径下的所有可执行文件,显然是不明智的
为了减少PATH的搜索,上一次搜索的内容能够被下一次执行重用
bash对从PATH中搜索得出的外部命令建立一个hash表,用于缓存
这个缓存是会话级别独立拥有的.不可以对其他终端共享,因为每个用户的PATH可能不同

[root@egon ~]# hostname # 执行一次PATH中的命令,linux系统就会将其缓存起来
egon.xxx.com

[root@egon ~]# hash   # 可以查看hash表的缓存
命中 命令
 2 /usr/bin/hostname
 1 /usr/bin/sed
 2 /usr/bin/man
 1 /usr/bin/ls
 
[root@egon ~]# hash -r  # 也可以清空掉
[root@egon ~]# hash
hash: 哈希表为空
 
#6、PATH
略
==========================优先级验证======================
===========alias>Compound Commands>function=========

#(1) 设置
[root@egon ~]# alias if="ls -l"
[root@egon ~]# function if(){
> echo "123"
> }

#(2) 验证alias的优先级最高
[root@egon ~]# if 
总用量 12
-rw-r--r-x. 1 root root 44 8月  13 19:36 1.sh
-rw-r--r-x. 1 root root 44 8月  13 19:36 2.sh
-rw-r--r-x. 1 root root 44 8月  13 19:37 3.sh
 
#(3) 取消别名,验证复合命令优先级>function
[root@egon ~]# unalias if
[root@egon ~]# if # 执行的是复合命令而不是函数
>
===========function>buitin=========

#(1) 设置
[root@egon ~]# function cd(){
> echo "自定义函数cd"
> }
[root@egon ~]#
[root@egon ~]# type cd
cd 是函数
cd ()
{
echo "自定义函数cd"
}

#(2) 验证
[root@egon ~]# cd # 执行的是函数
自定义函数cd
===========buitin>hash>PATH=========
#(1)最开始hash表内是没有任何命令的,优先级:builtin > PATH
[root@egon ~]# unset cd
[root@egon ~]# type cd
cd 是 shell 内嵌
[root@egon ~]# cat /usr/bin/cd
#!/bin/sh
echo "这是PATH内的一个cd脚本"
[root@egon ~]# chmod +x /usr/bin/cd
[root@egon ~]# cd # 执行的是内置命令,而不是PATH下的脚本cd
[root@egon ~]# cp /usr/bin/cd /usr/bin/hahaha
[root@egon ~]# hahaha
这是PATH内的一个cd脚本
[root@egon ~]#

#(2)后来hash表中有命令,但它缓存的是PATH中的命令,说白了,与PATH都是一样的东西,内置命令
的优先级肯定高于hash

# (3) 优先级hash>$PATH
[root@egon ~]# hash -r
[root@egon ~]# hostname
egon.xxx.com
[root@egon ~]# hash
命中 命令
 1 /usr/bin/hostname
[root@egon ~]# hostname
egon.xxx.com
[root@egon ~]# hash # 缓存命中2次
命中 命令
 2 /usr/bin/hostname

1.bash的特性

1、交互式环境下:命令和文件路径自动补全(使用tab键)
2、交互式环境下:命令历史记录

上下键翻阅
!历史命令编号
!命令前缀字符
!$上一条命令的参数

ll -dl !$

!!执行最后一条命令

!$ 执行最后一条命令的路径

[root@zabbix ~]# cd /
[root@zabbix /]# ll !$
结果显示根目录...

3、别名功能
alias、unalias、\命令

4、前后台作业:

&、nohup、 c、 z、bg %1、fg %1、kill %3、screen

# 示例1
sh-3.2# sleep 15 # ctrl+z暂停程序
^Z
[1]+  Stopped(SIGTSTP)     sleep 15
sh-3.2# jobs
[1]+  Stopped(SIGTSTP)     sleep 15
sh-3.2# bg %1 # 后台运行
[1] sleep 15 &
sh-3.2# jobs
[1]+  Running         sleep 15 &
sh-3.2# fg %1 # 前台运行
sleep 15
sh-3.2#
# 示例2:
sh-3.2# cat a.sh
sleep 15 && echo 123
sh-3.2# sh a.sh # 暂停
^Z
[1]+  Stopped(SIGTSTP)     sh a.sh
sh-3.2# bg %1 # 后台运行
[1] sh a.sh &
sh-3.2# fg %1 # 前台运行,也可以kill %1杀死
sh a.sh
123

5、输入输出重定向:

0、1、2

>、>>、2>、2>>、&>、cat >file1 <<EOF

6、支持管道:|

7、一行连接多条命令

;   #左边的命令执行无论是否成功都会执行右边

&&  #左边的命令执行成功才会执行右边
  make && make install

||  #左边的命令执行失败才会执行右边
  [ -defdg /home/egon ] || mkdir -p /home/egon 

8、支持通配符操作,例如*、?、[ ]、\ 等

三. 元字符

元字符指的是能够被shell解释的特殊字符,每一特殊字符都有其特殊含义

元字符属于shell这门编程语言的语法,被shell解释器解释的特殊字符

ps:grep命令解释的特殊符号=》正则表达式,正则与元字符中的符号都是公用的,但是表示的意义截然不同

为何要用元字符

让命令的操作更加丰富

1 `` 与$():取命令的结果

[root@localhost ~]# echo `pwd`
/root
[root@localhost ~]# echo (pwd)
/root

不一样的地方在于()可以嵌套,而``不能嵌套
[root@localhost ~]# echo (ls(pwd))

# 练习
[root@aliyun test]# touch $(date +%F)_bak.tar.gz
[root@aliyun test]# ls
2020-08-24_bak.tar.gz

2 ~家目录

3 .与..

4 !调用历史命令、取反

# 1、调用历史命令
[root@egon ~]# !1066

# 匹配最近的一次历史命令
[root@egon ~]# !ls 

# 2、取反1:对命令的结果取反
[root@egon ~]# ! pwd
/root
[root@egon ~]# echo $?
1

# 3、取反2:效果与^雷同
[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

[root@aliyun test]# ls -a /etc/skel/.[!.]*

5 @无特殊意义

6 #注释

7 $取变量值

[root@localhost ~]# x=1
[root@localhost ~]# echo $x
1

8 % - + * / 运算符

注意%可以与jobs配合“kill %工作号”杀后台进程。- 减号还有区间及cd - 回到上一级的意思

特殊 ++   +=
m++  (从0开始每次+1)
+=   (每次加上=号后面的数值)

# 数学运算
# 1、bc是比较常用的linux计算工具了,而且支持浮点运算:
[root@localhost ~]# res=`echo 1+1 | bc`
[root@localhost ~]# echo res
2
[root@localhost ~]# res=`echo 10 % 3 | bc`
[root@localhost ~]# echores
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 ~]# echores
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 ~]# echores
.83

[root@Centos7 test]# echo (echo "scale=2;33/100" | bc |cut -d. -f2)%
33%

# 2、expr不支持浮点数计算。而且要注意数字与运算符中的空格
[root@localhost ~]# res=`expr 5 / 3` # 不支持浮点计算
[root@localhost ~]# echores
1
[root@localhost ~]# res=`expr 1+1` # 注意:要有空格
[root@localhost ~]# echo res
1+1
[root@localhost ~]# res=`expr 1 + 1`
[root@localhost ~]# echores
2

# 3、(()) 同expr,不支持浮点数运算
[root@localhost ~]# echo((1+1))
2
[root@localhost ~]# echo ((1.0+2.0))
-bash: 1.0+2.0: 语法错误: 无效的算术运算符 (错误符号是 ".0+2.0")
[root@Centos7 test]# exprn+1  #  一定记得在运算符左右两侧加空格
10+1
     
# 4、[]同expr以及(()),不支持浮点运算
[root@localhost ~]# x=1
[root@localhost ~]# echo [x+1]
2

# 5、let 不支持浮点数运算,而且不支持直接输出,只能赋值
[root@Centos7 test]# age=18
[root@Centos7 test]# age=[age+1]
[root@Centos7 test]# echo age
19
[root@Centos7 test]# let age=age+1
[root@Centos7 test]# echoage
20
[root@Centos7 test]# let age+=1  # age=age+1
[root@Centos7 test]# echo age
21

[root@localhost ~]# let res=1+1
[root@localhost ~]# echores
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"
       
[root@aliyun test]# x=1
[root@aliyun test]# let x+=10
[root@aliyun test]# echox
11
# m++ 与 ++n区别
[root@Centos7 test]# let i++
[root@Centos7 test]# echo i
1
[root@Centos7 test]# let ++j
[root@Centos7 test]# echoj
1

[root@Centos7 test]# unset m
[root@Centos7 test]# unset n
[root@Centos7 test]# unset x
[root@Centos7 test]# unset y
[root@Centos7 test]# 
[root@Centos7 test]# let x=m++
[root@Centos7 test]# let y=++n
[root@Centos7 test]# 
[root@Centos7 test]# echo x
0
[root@Centos7 test]# echoy
1

9 ^ 同 ! 一样

10 & 后台运行

# 进程的运行状态: 运行态、就绪态、阻塞态
    并发:看起来是同时运行的
    并行:真正意义上的同时运行,只有多核才有并行的可能性

[root@localhost home]# echo "hello";sleep 3;echo "world" &

11 *任意多个字符

[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

12 ()在子shell中执行

#使用这种方式可以避免程序执行的结果影响当前shell变量.

[root@localhost ~]# (x=1)
[root@localhost ~]# echo $x
无结果

# 让umask的设置只在子shell中有效
[root@Centos7 ~]# umask
0022
[root@Centos7 ~]# (umask 666;touch {a..c}.txt)
[root@Centos7 ~]# touch d.txt
[root@Centos7 ~]# ll 
total 4
-rw-------. 1 root root 1523 Jun 29 23:38 anaconda-ks.cfg
----------  1 root root    0 Aug 25 18:53 a.txt
----------  1 root root    0 Aug 25 18:53 b.txt
----------  1 root root    0 Aug 25 18:53 c.txt
-rw-r--r--  1 root root    0 Aug 25 18:53 d.txt

13 _下划线:无特殊意义,可以用于名字的声明

[root@Centos7 ~]# tar czf /bak/`date +%F_%H:%M:%S`_etc.bak.gz /etc
tar: Removing leading `/' from member names
[root@Centos7 ~]# ls /bak/
2020-08-25_18:56:29_etc.bak.gz

14 =赋值,==判断相等性

# 条件1 == 1的左右两边必须有空格
[root@localhost ~]# [ 1 == 1 ]

# 判断上一条命令的结果是否为真,0=》true
[root@localhost ~]# echo $?  
0

15 | 管道:把一个进程的处理结果传递给另外一个进程

[root@localhost ~]# ps aux | grep python

# xargs参数传递,把上一个命令的结果作为下一个命令的参数
查找home下类型为目录,名字为test传递给ls执行
[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

16 \ 转义特殊字符

[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 \$RMBRMB

17 [ ] 匹配一个字符或者条件测试

18 引号

''  强引用(在单引号中都视为普通字符)
" " 弱引用 (在双引号中保留变量)
[root@localhost ~]# x=111
[root@localhost ~]# echo "x"
111
[root@localhost ~]# echo 'x'
$x

19 ; 与 && 与 | | 连接多条命令

[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

20 : 空命令,真值

[root@egon ~]# :
[root@egon ~]# echo $?
0

21 / 路径分隔符

22 { } 循环列表

[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
[root@localhost ~]# touch {1..3}{a..d}.txt
[root@localhost ~]# ls
1a.txt 1b.txt 1c.txt 1d.txt 2a.txt 2b.txt 2c.txt 2d.txt 3a.txt 3b.txt 3c.txt
3d.txt

#控制变量名的范围
[root@egon ~]# x=100
[root@egon ~]# echo {x}%   
100%

[root@Centos7 ~]# echonumRMB
无结果
[root@Centos7 ~]# echo ${num}RMB
30RMB

23 重定向

> >> 输出重定向
< << 输入重定向
> 覆盖  >> 追加

[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 </dev/zero >/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

24 ? 任意一个字符

[root@localhost ~]# ls ??.txt
aa.txt
[root@localhost ~]# ls a?c.txt
a1c.txt
[root@Centos7 test]# ls ????.txt
1111.txt

也可以用这种方式代替1个? :
[root@Centos7 test]# ls [].txt

22、范围中的任意一个字符 [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
Copyright © 2009 - Now . XPBag.com . All rights Reserved.
夜心的小站 » Shell编程bash原理及特性 元字符详解

提供最优质的资源集合

立即查看 了解详情