shell脚本基础——Shell运行原理+变量数组定义

Posted 何翰宇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell脚本基础——Shell运行原理+变量数组定义相关的知识,希望对你有一定的参考价值。

文章目录


一、shell介绍

1. 编程语言分类

1)编译型语言

程序在执行之前需要一个专门的编译过程,把程序编译成机器语言也就是二进制文件,运行时不需要重新翻译,直接使用编译的二进制文件运行就好了。程序执行效率较高,依赖编译器。比如Java语言,一次编译到处运行的说法,把编译出来的二进制字节码文件可以运行在不同平台的JVM上。

2)解释型语言

程序不需要编译,程序在运行时由解释器翻译成机器语言,每执行一次都要翻译一次,因此效率比较低,shell就是解释语言,也可以叫做脚本语言。

2. shell运行原理


inux严格意义上说的是一个操作系统,我们称之为“核心(kernel) “ ,但我们一般用户,不能直接使用kernel。
而是通过kernel的“外壳”程序,也就是所谓的shell,来与kernel沟通。如何理解?为什么不能直接使用kernel?

从技术角度, Shell的最简单定义:命令行解释器(command Interpreter)主要包含:

将使用者的命令翻译给核心(kernel)处理。
同时,将核心的处理结果翻译给使用者

对比windows GUI,我们操作windows 不是直接操作windows内核,而是通过图形接口,点击,从而完成我们的
操作(比如进入D盘的操作,我们通常是双击D盘盘符.或者运行起来一个应用程序)

Shell运行原理:

  • 创建子进程,让子进程进行命令行解释
  • 子进程出现任何问题,都不影响父进程Shell

这样做的好处就是即使出现问题,也不影响操作系统。同样shell外壳,还可以拦截非法执行,防止用户破坏操作系统。


就好比Windows的微信突然异常退出,也就是终止了运行,但微信是一个子子进程,是不会影响其它软件运行的。

3. 小结

Shell只是所有外壳程序的统称,例如在centos 7当中的外壳程序名叫bash(默认的)。
Shell就是一个中间人,来实现人与操作系统进行对话。

简单来说就是将需要执行的命令保存到文本中,按照顺序执行。它是解释型的,意味着不需要编译
Shell的种类:

[root@MissHou ~]# cat /etc/shells 
/bin/sh			#是bash shell的一个快捷方式
/bin/bash		#bash shell是大多数Linux默认的shell,包含的功能几乎可以涵盖shell所有的功能
/sbin/nologin	#表示非交互,不能登录操作系统
/bin/dash		#小巧,高效,功能相比少一些
/bin/tcsh		#是csh的增强版,完全兼容csh
/bin/csh		#具有C语言风格的一种shell,具有许多特性,但也有一些缺陷

4. 什么时候用到脚本?

重复化、复杂化的工作,通过把工作的命令写成脚本,以后仅仅需要执行脚本就能完成这些工作。

  1. 自动化分析处理
  2. 自动化备份
  3. 自动化批量部署安装
  4. 等等…

二、Shell脚本的基本用法

1. 脚本的基本写法

#!魔法字符,指定脚本代码执行的程序。即它告诉系统这个脚本需要什么解释器来执行,也就是使用哪一种Shell

#!/bin/bash
// 脚本第一行,一定要写 #! 指定脚本代码执行程序 
//以下内容是对脚本的基本信息的描述
# Name:名字
# Desc:脚本描述
# Path:存放路径
# Usage:脚本用法
# Update:脚本更新时间

// 下面写脚本的具体内容
....

2. 脚本执行方法

1)标准脚本执行方法

建议使用,魔法字节指定的程序会生效

[root@lamp tmp]# cat shell.sh
#!/bin/bash

# Name: 测试
# Desc: 演示脚本使用
# Path: /root/tmp
# Usage: 测试
# Update: 2020/07/27

date
hostname 
# 给脚本文件加上可执行权限
[root@lamp tmp]# chmod +x shell.sh
# 执行
[root@lamp tmp]# ./shell.sh
Wed Jul 27 20:19:42 CST 2022
lamp

2)非标准的执行方法

不建议使用,魔法字节指定的程序不会运

[root@lamp tmp]# bash shell.sh
Wed Jul 27 20:36:43 CST 2022
lamp
[root@lamp tmp]# sh shell.sh
Wed Jul 27 20:36:48 CST 2022
lamp
[root@lamp tmp]# bash -x shell.sh
+ date
Wed Jul 27 20:36:57 CST 2022
+ hostname
lamp
-x:一般用于排错,查看脚本的执行过程
-n:用来查看脚本的语法是否有问题

注意:如果脚本没有加可执行权限,不能使用标准的执行方法执行,bash 1.sh

[root@lamp tmp]# source shell.sh
Wed Jul 27 20:37:10 CST 2022
lamp
[root@lamp tmp]# . shell.sh
Wed Jul 27 20:37:14 CST 2022
lamp

source 和 . 表示读取文件,执行文件里的命令

3. bash基本特性

1)常用快捷键

^表示Ctrl

^c   			终止前台运行的程序
^z	  			将前台运行的程序挂起到后台
^d   			退出 等价exit
^l   			清屏 
^a |home  	光标移到命令行的最前端
^e |end  	光标移到命令行的后端
^u   			删除光标前所有字符
^k   			删除光标后所有字符
^r	 			搜索历史命令

2)常用的通配符

*:	匹配0或多个任意字符
?:	匹配任意单个字符
[list]:	匹配[list]中的任意单个字符
[!list]: 匹配除list中的任意单个字符
string1,string2,...:匹配string1,string2或更多字符串

测试

[root@lamp tmp]# touch file1..3
[root@lamp tmp]# touch file1..13.c
[root@lamp tmp]# ls file*

[root@lamp tmp]# ls *.c

[root@lamp tmp]# ll file?

[root@lamp tmp]# ll file?.c

[root@lamp tmp]# ll file[1023].c

[root@lamp tmp]# ll file[0-13].c

[root@lamp tmp]# ll file1[0-9].c

[root@lamp tmp]# ll file?[1-13].c

[root@lamp tmp]# ll file[1,2,3,10,11,12].c

[root@lamp tmp]# ll file1,2,3,10,11,12.c

[root@lamp tmp]# ll file11,12,1,2.c

[root@lamp tmp]# ll file1..10.c

4. bash中的引号

  • 双引号" :会把引号的内容当成整体来看待,允许通过$符号引用其他变量值
  • 单引号' :会把引号的内容当成整体来看待,禁止引用其他变量值,shell中特殊符号都被视为普通字符
  • 反撇号 ` :反撇号和$()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用
[root@lamp ~]# echo "$(hostname)"
lamp
[root@lamp ~]# echo '$(hostanme)'
$(hostanme)
[root@lamp ~]# echo "hello world"
hello world
[root@lamp ~]# echo 'hello world'
hello world
[root@lamp ~]# echo $(date +%F)
2022-07-28
[root@lamp ~]# echo `echo $(date +%F)`
2022-07-28
[root@lamp ~]# echo `date +%F`
2022-07-28
[root@lamp ~]# echo `echo `date + %F``
date + %F
[root@lamp ~]# echo $(echo `date +%F`)
2022-07-28

三、变量的定义

1. 本地变量(临时变量)

当前用户自定义的变量,当前进程中有效,其它进程及当前进程的子进程无效。

[root@lamp ~]# ps
   PID TTY          TIME CMD
  7272 pts/0    00:00:00 bash
  7290 pts/0    00:00:00 ps
[root@lamp ~]# tmp=100
[root@lamp ~]# echo $tmp
100
[root@lamp ~]# /bin/bash
# 可以理解为7291的bash就是 7272的子进程
[root@lamp ~]# ps
   PID TTY          TIME CMD
  7272 pts/0    00:00:00 bash
  7291 pts/0    00:00:00 bash
  7302 pts/0    00:00:00 ps
[root@lamp ~]# echo $tmp



2. 环境变量

当前进程有效,并且能够被子进程调用。

  • env:查看当前用户的环境变量
  • set:查询当前用户的所有变量(临时变量和环境变量)
  • export:将当前变量变成环境变量(临时)

临时添加环境变量,关闭终端后失效

[root@lamp ~]# tmp=100
[root@lamp ~]# set | grep tmp
tmp=100
[root@lamp ~]# env | grep ^t
# 将临时变量变成环境变量
[root@lamp ~]# export tmp
[root@lamp ~]# env | grep ^t
tmp=100

永久添加环境变量
修改配置文件/etc/profile或者~/.bashrc

export tmp=100

系统中有一个变量PATH,环境变量,当输入一个命令,它就会去PATH这些指定的路径中找,看看是否有这个命令

[root@lamp ~]# env | grep ^PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:/root/bin
[root@lamp ~]# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
[root@lamp ~]# mysql
-bash: mysql: command not found

[root@lamp ~]# PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:/root/bin
[root@lamp ~]# mysql -uroot -p
Enter password:

3. 全局变量

全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用。

用户家目录里有三个影藏文件

  • .bash_logout:每个用户退出当前shell时最后读取的文件
  • .bash_profile:当前用户的环境变量
  • .bashrc:当前用户的bash信息(aliase、umask等)

全局变量的一些配置文件

/etc/bashrc             使用bash shell用户全局变量(比如定义别名)
/etc/profile   		   系统和每个用户的环境变量信息(低定义环境变量)

用户登录系统读取相关文件的顺序

  1. /etc/profile
  2. ~/.bash_profile
  3. ~/.bashrc
  4. /etc/bashrc
  5. ~/.bash_logout

4. 系统内置变量

内置bash中的变量,shell本身已经固定好了它的名字和作用。

  • $?
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
 若退出状态值为0,表示命令运行成功
 若退出状态值为127,表示command not found
 若退出状态值为126,表示找到了该命令但无法执行(权限不够)
 若退出状态值为1&2,表示没有那个文件或目录

[root@lamp ~]# date
Thu Jul 28 10:29:22 CST 2022
[root@lamp ~]# echo $?
0
[root@lamp ~]# a
-bash: a: command not found
[root@lamp ~]# echo $?
127
[root@lamp ~]# cat qwq
cat: qwq: No such file or directory
[root@lamp ~]# echo $?
1

  • $$
$$:当前所在进程的进程号     echo $$   eg:kill -9 `echo $$`  = exit   退出当前会话
$!:后台运行的最后一个进程号  (当前终端)  # gedit &
!$	调用最后一条命令历史中的参数
!!	调用最后一条命令历史

[root@lamp ~]# echo $$
7487
[root@lamp ~]# /bin/bash
[root@lamp ~]# echo $$
7649
[root@lamp ~]# exit
exit
[root@lamp ~]# echo $$
7487

[root@lamp ~]# date
Thu Jul 28 10:33:46 CST 2022
[root@lamp ~]# !!
date
Thu Jul 28 10:33:49 CST 2022
[root@lamp ~]# ls -l
[root@lamp ~]# !$
-l
-bash: -l: command not found
[root@lamp ~]# $! 
-bash: 7667: command not found



脚本参数相关


$#:脚本后面接的参数的个数
$*:脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开
$@: 脚本后面所有参数,参数是独立的,也是全部输出

$*和$@的区别:
$*	:表示将变量看成一个整体
$@	:表示变量是独立的

$0:当前执行的进程/程序名  echo $0	    
$1~$9 位置参数变量
$10~$n 扩展位置参数变量  第10个位置变量必须用大括号括起来

编写测试脚本


[root@lamp tmp]# clear
[root@lamp tmp]# cat test.sh
#!/bin/bash
#test

echo "\\$0 = $0"
echo "\\$# = $#"
echo "\\$* = $*"
echo "\\$@ = $@"
echo "\\$1 = $1"
echo "\\$2 = $2"
echo "\\$3 = $3"
echo "\\$10 = $10"
echo "\\$11 = $11"

测试

[root@lamp tmp]# ./test.sh 1 2
$0 = ./test.sh
$# = 2
$* = 1 2
$@ = 1 2
$1 = 1
$2 = 2
$3 = 
$10 = 
$11 = 
[root@lamp tmp]# ./test.sh 1 2 3 4 5 6 7
$0 = ./test.sh
$# = 7
$* = 1 2 3 4 5 6 7
$@ = 1 2 3 4 5 6 7
$1 = 1
$2 = 2
$3 = 3
$10 = 
$11 = 
[root@lamp tmp]# ./test.sh 1 2 3 4 5 6 7 a b c d
$0 = ./test.sh
$# = 11
$* = 1 2 3 4 5 6 7 a b c d
$@ = 1 2 3 4 5 6 7 a b c d
$1 = 1
$2 = 2
$3 = 3
$10 = c
$11 = d
[root@lamp tmp]# ./test.sh 5 6 7 8 9 10 11 12 a b c d
$0 = ./test.sh
$# = 12
$* = 5 6 7 8 9 10 11 12 a b c d
$@ = 5 6 7 8 9 10 11 12 a b c d
$1 = 5
$2 = 6
$3 = 7
$10 = b
$11 = c

5. 什么时候需要定义变量

  • 如果某个内容需要多次使用,并且在代码中重复出现,那么可以用变量代表该内容。这样在修改内容的时候,仅仅需要修改变量的值。
  • 在代码运作的过程中,可能会把某些命令的执行结果保存起来,后续代码需要使用这些结果,就可以直接使用这个变量。

6. 变量的定义规则

默认情况下,shell里定义的变量是不分类型的,可以给变量赋与任何类型的值;等号两边不能有空格,对于有空格的字符串做为赋值时,要用引号引起来

# 变量名可以是字母或数字或下划线,但是不能以数字开头或者特殊字符,变量名是区分大小写的
变量名=变量值

1)变量的获取方式

$变量名 和 $变量名 获取变量
[root@lamp tmp]# tmp=123456789
[root@lamp tmp]# echo $tmp
123456789
[root@lamp tmp]# echo $tmp
123456789
[root@lamp tmp]# echo $tmp:3:5 # 3表示从第4个字符开始,5表示后面5个字符
45678

2)执行结果保存到变量

[root@lamp tmp]# result=`date +%F`
[root@lamp tmp]# echo $result
2022-07-28
[root@lamp tmp]# name=$(hostname)
[root@lamp tmp]# echo $name
lamp

3)定义有类型变量(declare)

通过declare可以定义有类型的变量
常用选项:

  • -i:将变量定义为整数
  • -r:使变量变成只读(也就是不可以改变的)
  • -x:标记变量通过环境导出 export

示例


[root@lamp tmp]# declare -i a=10
[root@lamp tmp]# declare -i b=20
[root@lamp tmp]# declare -i c=$a+$b
[root@lamp tmp]# echo $c
30
# -x 标记变量通过环境导出
[root@lamp tmp]# str=abc
[root@lamp tmp]# export str
[root@lamp tmp]# env | grep ^s
str=abc
[root@lamp tmp]# declare -x string=aaba
[root@lamp tmp]# env | grep ^s
string=aaba
str=abc
# -r只读不能修改
[root@lamp tmp]# declare -r tmp=123
[root@lamp tmp]# tmp=321
-bash: tmp: readonly variable



4)取消变量

[root@lamp tmp]# tmp=123
[root@lamp tmp]# echo $tmp
123
[root@lamp tmp]# unset tmp
[root@lamp tmp]# echo $tmp


四、数组定义

数组分为普通数组关联数组

  • 普通数组:只能使用整数作为数组索引(元素的下标)
  • 关联数组:可以使用字符串作为数组索引(元素的下标)

1. 普通数组定义

普通数组定义:用括号来表示数组,数组元素(变量)用“空格”符号分割开。

1)普通数组赋值

  • 一次赋一个值
变量名=变量值
 [root@lamp tmp]# arr[0]=1
 [root@lamp tmp]# arr[1]=a
 [root@lamp tmp]#  arr[3]=6b

  • 一次赋多个值
[root@lamp tmp]# arr2=(test1 test2 test3 666)
[root@lamp tmp]# arr3=(`hostname`)
[root@lamp tmp]# arr4=(`date +%F`)
[root@lamp tmp]# arr5=(1 2 3 "hello world" [11]=java)

2)读取数组

$arr[i]:i为数组下标
使用@或者*可以获取数组中的所有元素

[root@lamp tmp]# echo $arr5[0] # 获取数组里的第一个元素

[root@lamp tmp]# echo $arr5[*] # 获取数组里的所有元素

[root@lamp tmp]# echo $#arr5[*] # 获取数组里所有元素个数

[root@lamp tmp]# echo $!arr5[@] # 获取数组元素的索引下标

[root@lamp tmp]# echo $arr5[@]:2:3 # 访问指定的元素;2代表从下标为2的元素开始获取;3代表获取后面几个元素

3)查看普通数组信息

通过命令:declare -a查看所有普通数组信息

[root@lamp tmp]# declare -a

declare -a arr='([0]="1" [1]="a" [3]="6b")'
declare -a arr2='([0]="test1" [1]="test2" [2]="test3" [3]="666")'
declare -a arr3='([0]="lamp")'
declare -a arr4='([0]="2022-07-28")'
declare -a arr5='([0]="1" [1]="2" [2]="3" [3]="hello world" [11]="java")'

2. 关联数组

1)关联数组声明

语法:declare -A [数组名]

[root@lamp tmp]# declare -A array1
[root@lamp tmp]# declare -A array2
[root@lamp tmp]# declare -A array3

2)数组赋值

  • 一次赋一个值
[root@lamp tmp]# array1[linux]=one
[root@lamp tmp]# array1[java]=two
[root@lamp tmp]# array1[c++]=three

  • 一次赋多个值
[root@lamp tmp]# array2=([one]=linux [two]=java [three]=c++ [four]="hello world")

3)查看关联数组

[root@lamp tmp]# declare -A
declare -A array1='([c++]="three" [java]="two" [linux]="one" )'
declare -A array2='([four]="hello world" [one]="linux" [two]="java" [three]="c++" )'

[root@lamp tmp]# echo $array1[java] # 获取java索引的值
two
[root@lamp tmp]# echo $array1[linux]
one
[root@lamp tmp]# echo $array1[*] # 获取所有值
three two one
[root@lamp tmp]# echo $!array1[*] # 获取所有索引名
c++ java linux
[root@lamp tmp]# echo $#array1[*] # 获取元素个数
3
[root@lamp tmp]# echo $#array2[*]
4
[root@lamp tmp]# echo $!array2[*]
four one two three

4)交互式定义变量的值 (从键盘输入)

命令:read [选项] 选项值

常用选项:

  • -p:提示信息
  • -n:字符数(限制变量值的字符数量)
  • -s:不显示
  • -t:超时(默认单位秒)(限制输入变量值的超时时间)
[root@lamp tmp]# read -p "input password:" passwd
input password:123456
[root@lamp tmp]# echo $passwd
123456

[root@lamp tmp]# read -p "inpute username password:" name passwd
inpute username password:test 123   

[root@lamp tmp]# echo $name
test
[root@lamp tmp]# echo $passwd
123

[root@lamp tmp]# read -p "inpute username password:" name passwd -t 4
inpute username password:123 456
-bash: read: `-t': not a valid identifier
[root@lamp tmp]# read -p "inpute username password:" name passwd -n 4
inpute username password:test 12345 
-bash: read: `-n': not a valid identifier

从文件总读取

[root@lamp tmp]# read -p "inpute username password:" name passwd -t 4
inpute username password:123 456
-bash: read: `-t': not a valid identifier
[root@lamp tmp]# read -p "inpute username password:" name passwd -n 4
inpute username password:test 12345 
-bash: read: `-n': not a valid identifier

5)其它

  • 取出一个目录下的目录和文件:dirnamebasename
[root@lamp tmp]# temp=/root/tmp/file/test.txt
[root@lamp tmp]# dirname $temp # 去除目录
/root/tmp/file
[root@lamp tmp]# basename $temp # 取出文件
test.txt
  • 变量"内容"的删除和替换
    1)一个“%”代表从右往左去掉一个/key/
    2)两个“%%”代表从右往左最大去掉/key/
    3)一个“#”代表从左往右去掉一个/key/
    4)两个“##”代表从左往右最大去掉/key/
[root@lamp tmp]# url=www.baidu.com
[root@lamp tmp]# echo $#url
13
[root@lamp tmp]# echo $url#*.
baidu.com
[root@lamp tmp]# echo $url##*.
com
[root@lamp tmp]# echo $url%.*
www.baidu
[root@lamp tmp]# echo $url%%.*
www

五、四则运算

算术运算:默认情况下,shell就只能支持简单的整数运算

+ - * / %

Bash shell 的算术运算有四种方式:

  1. 使用 $(( ))
  2. 使用$[ ]
  3. 使用 expr 外部程式
  4. 使用let 命令
[root@lamp tmp]# a=1
[root@lamp tmp]# b=2
[root@lamp tmp]# echo $((a+b))
3
[root@lamp tmp]# echo $[a+b]
3
[root@lamp tmp]# let c=a+b
[root@lamp tmp]# echo $c
3

# 需要转义
[root@lamp tmp]# expr 1 \\+ 4
5
[root@lamp tmp]# expr 1 \\* 4
4
[root@lamp tmp]# expr 10 \\% 3
1


但是shell每没法进行小数计算
可以按安装bc工具,一个小型计算器:yum -y install bc

[root@lamp ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
1+1
2
4.6/2
2
5%2
1
^C
(interrupt) Exiting bc.

以上是关于shell脚本基础——Shell运行原理+变量数组定义的主要内容,如果未能解决你的问题,请参考以下文章

linux(三十一)shell脚本基础知识整理

SHELL脚本--shell数组基础

shell脚本基础--数组

shell脚本进阶之数组基础

shell基础--基本指令

shell脚本基础