个人用云计算学习笔记 --22(Shell 编程-1)
文章目录
- Shell 脚本
- Shell 脚本
- 用source 执行脚本和用bash执行脚本的区别
- 1.脚本开头
- 2 脚本注释
- 3 bash 与 sh 的区别
- Shell 变量基础知识
- 1.Shell 变量
- 1.1什么是变量
- 1.2 Shell 变量的特性
- 1.3 变量类型
- 2 环境变量
- 2.1 设置环境变量
- 2.2 显示与取消环境变量
- Shell 变量进阶知识
- 1.Shell 中特殊变量
- 1.1 Shell 位置参数变量
- 1.2 Shell 进程中的特殊状态变量
- $?
- $$(进阶)
- $!(进阶)
- $_
- 2 Shell 内置变量命令
- 2.1 echo
- 2.2 eval(进阶)
- 2.3 read
- 2.4 exec(进阶)
- 2.5 shift(进阶)
- 变量的数值计算实践
- 1 算术运算符
- 2 (()) 双小括号数值运算命令
- 3 let 命令
- 4 expr 命令(进阶)
- 5 bc 命令
- 6 awk 命令实现计算
- 7 $[] 符号的运算
Shell 脚本
Shell 脚本
在 Linux 系统中, Shell 脚本通常是在编辑器 vi/vim 中编写的,由UNIX/Linux命令、bash Shell 命令、程序结构控制语句和注释等内容组成。这里推荐用Linux自带的功能更强大的vim编辑器来编写,可以事先做一个别名alias vi=’vim’
,并使其永久生效,这样以后习惯输入 vi 的读者也就可以直接调用 vim
编辑器了。设置方法如下:
[root@shell ~]# echo "alias vi='vim'" >> /etc/bashrc
[root@shell ~]# source /etc/bashrc
source
命令用于在当前 Shell 会话中执行指定文件中的命令或脚本
用source 执行脚本和用bash执行脚本的区别
source
(或其简写 .
)与 bash
执行脚本的核心区别在于是否创建新的 Shell 进程,这会导致执行效果有显著差异:
特性 | source 脚本.sh 或 . 脚本.sh | bash 脚本.sh 或 ./脚本.sh |
---|---|---|
运行环境 | 在当前 Shell 进程中执行 | 创建新的子 Shell 进程执行 |
环境变量影响 | 脚本中修改的环境变量会影响当前 Shell | 环境变量仅在子 Shell 中有效,不影响当前 Shell |
函数 / 别名生效范围 | 脚本中定义的函数 / 别名在当前 Shell 可用 | 仅在子 Shell 中有效,当前 Shell 不可用 |
脚本权限要求 | 不需要执行权限(chmod +x ) | 必须有执行权限(或通过解释器调用) |
退出行为 | 脚本中执行 exit 会退出当前 Shell | 仅退出子 Shell,不影响当前 Shell |
1.脚本开头
一个规范的 Shell 脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash 的编程一般为:
#!/bin/bash
或
#!/bin/sh
其中,开头的"#!“字符又称为幻数(其实叫什么都无所谓,知道它的作用就好),在执行bash脚本的时候,内核会根据”#!"字符后的解释器来确定该用哪个程序解释这个脚本中的内容。
注意,这一行必须位于每个脚本顶端的第一行,如果不是第一行则为脚本注释行,
[root@server ~]$ vim script.sh
#!/bin/bash
echo "Hello World !"
2 脚本注释
在 Shell 脚本中,跟在# 后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当作程序来执行,仅仅是给开发者和使用者看的,系统解释器是看不到的,更不会执行。
注释尽量不要用中文,在脚本中最好也不要有中文。
3 bash 与 sh 的区别
早期的bash与sh稍有不同,但大多数脚本都可以不加修改地在sh上运行。
sh为bash的软链接,大多数情况下,脚本的开头使用 #!/bin/bash
和 #!/bin/sh
是没有区别的,但更规范的写法是在脚本的开头使用 #!/bin/bash
。
Shell 变量基础知识
1.Shell 变量
1.1什么是变量
简单地说,变量名是用一个固定的字符串(字符、数字和下划线的组合,不能以数字开头)代替更多、更复杂的内容,该内容里可能还会包含变量、路径、字符串等其他的内容。
变量是暂时存储数据的地方及数据标记,所存储的数据存在于内存空间中,通过正确地调用内存空间中变量的名字就可以取出与变量对应的数据。
1.2 Shell 变量的特性
默认情况下,在bash Shell 中是不会区分变量类型的。如果需要指定 Shell 变量的类型,也可以使用 declare
命令定义变量的类型,但在一般情况下没有这个需求。
1.3 变量类型
变量根据范围可分为两类:
-
全局变量,在创建它们的 Shell 及其派生出来的任意子进程 Shell 中使用。
-
局部变量,只能在创建它们的 Shell 函数或 Shell 脚本中使用。
变量根据是否是用户自定义也可分为两类:
- 普通变量:也称为常规变量,由开发者在开发脚本程序时创建。
- 环境变量:定义shell 执行环境。环境变量又可分为自定义环境变量和bash内置的环境变量。
2 环境变量
环境变量一般是指用export内置命令导出的变量,用于定义 Shell 的运行环境,保证 Shell 命令的正确执行。
部分bash环境变量展示:
- EDITOR,默认编辑器。
- HISTFILE,历史文件位置。
- HISTSIZE,历史命令个数。
- PS1,命令行提示符。
- LANG,Shell 环境语言。
- PATH,命令搜素路径。
- HOME,当前用户家目录。
- USER,当前用户。
- PWD,当前 Shell 路径。
- IFS,字符串分隔符。
在查看设置的变量时,有3个命令可以显示变量的值:
- set 命令,输出所有的变量,包括全局变量和局部变量。
- env 命令,只显示全局变量,包括shell的环境。
- declare 命令,输出所有的变量、函数、整数和已经导出的变量。
set -o
命令显示bash Shell 的所有参数配置信息。
2.1 设置环境变量
如果想要设置环境变量,就要在给变量赋值之后或在设置变量时使用export
命令,具体设置见下文的示例。其实,除了 export
命令,带-x
选项的declare
内置命令也可以完成同样的功能(注意:此处不要在变量名前面加$)。
export
命令和 declare
命令的格式如下:
export 变量名=value
变量名=value ; export 变量名
declare -x 变量名=value
示例:
export NAME=laoma
declare -x NAME=laoma
NAME=laoma;export NAME
下面来看看让环境变量永久生效的常用设置文件。
-
用户的环境变量配置文件:
~/.bash_profile
和~/.bashrc
。推荐在(~/.bashrc)文件中设置。 -
全局环境变量的配置文件:
/etc/profile
、/etc/bashrc
、/etc/profile.d/
。**推荐在 /etc/bashrc 文件中设置。**若要在登录后初始化或显示加载内容,则把脚本文件放在/etc/profile.d/
下即可(无须加执行权限)。
2.2 显示与取消环境变量
- 通过
echo
或printf
命令打印环境变量。 - 用
env
或set
显示默认的环境变量。 - 用
unset
消除本地变量和环境变量。
Shell 变量进阶知识
1.Shell 中特殊变量
1.1 Shell 位置参数变量
在 Shell 中存在一些特殊且重要的变量,例如:$0
、$1
,我们称之为位置参数变量。要从命令行、函数或脚本执行等处传递参数时,就需要在 Shell 脚本中使用位置参数变量。
部分位置参数变量如下:
- $0,获取当前执行的 Shell 脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径。
- **n∗∗,获取当前执行的Shell脚本的∗∗第n个参数值∗∗。如果n大于9,则用大括号括起来,例如n**,获取当前执行的 Shell 脚本的**第n个参数值**。如果n大于9,则用大括号括起来,例如n∗∗,获取当前执行的Shell脚本的∗∗第n个参数值∗∗。如果n大于9,则用大括号括起来,例如{10},接的参数以空格隔开。
- $#,获取当前执行的 Shell 脚本后面接的参数数量。
- **∗∗∗,获取当前Shell脚本所有传参的参数,不加引号和‘***,获取当前 Shell 脚本所有传参的参数,不加引号和`∗∗∗,获取当前Shell脚本所有传参的参数,不加引号和‘@
相同;如果给
∗‘加上双引号,例如:‘"*`加上双引号,例如:`"∗‘加上双引号,例如:‘"*",则表示将所有的参数视为单个字符串,相当于
$1 $2 $3`。 - **@∗∗,获取当前Shell脚本所有传参的参数,不加引号和@**,获取当前 Shell 脚本所有传参的参数,不加引号和@∗∗,获取当前Shell脚本所有传参的参数,不加引号和*相同;如果给@加上双引号,例如:‘@加上双引号,例如:`@加上双引号,例如:‘@
,则表示将所有的参数视为不同的独立字符串,相当于
"$1"、“$2”、“$3”…`,这是将多参数传递给其他程序的最佳方式,因为它会保留所有的内嵌在每个参数里的任何空白。
示例1:
示例2:ssh_ctl 内容如下
#!/bin/bash
systemctl $1 sshd
执行效果如下:
[root@server ~]$ ssh_ctl stop
[root@server ~]$ ssh_ctl status
[root@server ~]$ ssh_ctl start
1.2 Shell 进程中的特殊状态变量
$?
作用:获取执行上一个指令的执行状态返回值:0为成功,非零为失败,这个变量最常用。
# man ls查看,退出码含义
[root@server ~]$ man ls
......Exit status:0 if OK,1 if minor problems (e.g., cannot access subdirectory),2 if serious trouble (e.g., cannot access command-line argument).
-
0
(成功):表示
ls
命令完全正常执行,没有遇到任何问题。例如,成功列出了指定目录的内容。 -
1
(轻微问题):表示命令执行过程中遇到了一些非致命的小问题,但整体仍能完成主要功能。
举例:尝试列出某个目录时,其中一个子目录没有访问权限(无法读取),但其他内容正常显示。
-
2
(严重错误):表示命令执行失败,通常是由于严重问题导致无法完成核心功能。
举例:指定的文件 / 目录不存在、命令参数错误,或没有权限访问命令行中指定的路径。
$$(进阶)
作用:获取当前执行的 Shell 脚本的进程号(PID),这个变量不常用,了解即可。
[root@server ~]$ echo $$
2447
[root@server ~]$ kill -9 $$Connection closed by foreign host.Disconnected from remote host(10.1.8.88) at 21:00:36.Type `help' to learn how to use Xshell prompt.
$!(进阶)
作用:获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可。
[root@server ~]$ md5sum /dev/zero &
[1] 2611
[root@server ~]$ echo $!
2611
[root@server ~]$ ps o pid,%cpu,%mem,command $!PID %CPU %MEM COMMAND2611 99.1 0.0 md5sum /dev/zero
[root@server ~]$ kill $!
[1]+ Terminated md5sum /dev/zero
$_
作用:获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可。
[root@server ~]$ ls /etc/hosts /etc/fstab /etc/hostname
/etc/fstab /etc/hostname /etc/hosts
[root@server ~]$ cat $_
laoma-shell
[root@server ~]$ cat /etc/hostname
laoma-shell
2 Shell 内置变量命令
2.1 echo
echo命令参数选项:
- -n,不换行输出内容。
- -e,解析转义字符(见下面的字符)
转义字符:
- \n,换行。
- \t,制表符(tab)。
- \b,退格。
2.2 eval(进阶)
当 Shell 程序执行到 eval 语句时, Shell 读入参数args,并将它们组合成一个新的命令,然后执行。
示例1:
[root@server ~]$ vim noeval.sh
echo \$$#
[root@server ~]$ bash noeval.sh hello world
$2[root@server ~]$ vim eval.sh
eval "echo \$$#"
[root@server ~]$ bash eval.sh hello world
world
示例2:
[root@server ~]$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-LmBsuYheypEC/agent.1589; export SSH_AUTH_SOCK;
SSH_AGENT_PID=1590; export SSH_AGENT_PID;
echo Agent pid 1590;[root@server ~]$ eval $(ssh-agent)
Agent pid 1598
# 等效于
[root@server ~]$ ssh-agent > /tmp/env
[root@server ~]$ source /tmp/env
[root@server ~]$ rm -f /tmp/env
2.3 read
从标准输入读取字符串等信息, 传给 Shell 程序内部定义的变量。
示例
#read 命令实践
[root@shell ~]# cat useradd.sh
#!/bin/bash# 提供用户名
read -p "请输入用户名:" user_name# 提供用户性别
read -p "请输入用户性别:" user_sex# 提供用户年龄
read -p "请输入用户年龄:" user_age# 提供用户密码,-s 选项,实现不显示用户密码
read -sp "请输入用户密码:" user_pass
echo# 添加用户
useradd ${user_name}
# 设置用户密码
echo ${user_pass} | passwd --stdin ${user_name}# 将用户信息存储到/etc/users.info中
echo ${user_name}:x:${user_sex}:${user_age} >> /etc/users.info# 打印用户信息存储到/etc/users.info中
echo "用户信息已经存储到/etc/users.info中."
2.4 exec(进阶)
exec 命令能够在不创建新的子进程的前提下, 转去执行指定的命令, 当指定的命令执行完毕后, 该进程 (也就是最初的 Shell ) 就终止了。
2.5 shift(进阶)
shift 语句会按如下方式重新命名所有的位置参数变量,即$2成为$1、$3成为2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数#减1,直到减到0为止。
如果脚本有3个参数,那么执行一次shift后 ,$3就变成了$2,$2变成了$1,原先的$1就消失了。
变量的数值计算实践
1 算术运算符
常见的 Shell 算术运算符:
- +、-,一元正号和负号。
- +、-,加法和减法。
- *、/、%,乘法、除法、取余(取模)。
- **,幂运算。
- ++、–,增加及减少,可前置也可放在变量结尾。
- !、&&、||,逻辑非(取反)、逻辑与(and)、逻辑或(or)。
- <、<=、>、>=,比较符号(小于、小于等于、大于、大于等于)。
- ==、!=、=,比较符号(相等、不相等,对于字符串也可以表示相当于)。
- <<、>>,向左移位、向右移位。
- ~、|、&、^,按位取反、按位异或、按位与、按位。
- =、+=、-=、*=、/=、%=,赋值运算符,例如
a+=1
相当于a=a+1
,a-=1
相当于a=a-1
。
Shell 中常见的算术运算命令:
- (()),用于整数运算的常用运算符,效率很高。
- let,用于整数运算,类似于
(())
。 - expr,可用于整数运算,但还有很多其他的额外功能。
- bc,Linux下的一个计算器程序(适合整数及小数运算)。
- $[],用于整数运算。
- awk,awk 既可以用于整数运算,也可以用于小数运算。
- declare,定义变量值和属性,-i参数可以用于定义整形变量,做运算。
2 (()) 双小括号数值运算命令
双小括号 (()) 的作用是进行数值运算与数值比较,它的效率很高,用法灵活,是企业场景运维人员经常采用的运算操作符。
双小括号 (()) 的操作方法:
-
((i=i+1)),此种书写方法为运算后赋值法,即将i+1的运算结果赋值给变量i。
注意:不能用
echo ((i=i+l))
输出表达式的值,可以用echo $((i=i+l))
输出其值。 -
**i=((i+1))∗∗,可以在‘(())‘前加‘((i+1))**,可以在 `(())` 前加 `((i+1))∗∗,可以在‘(())‘前加‘` 符,表示将表达式运算后赋值给i。
-
(( 8>7 && 5==5)),可以进行比较操作,还可以加入逻辑与和逻辑或,用于条件判断。
-
**echo ((2+1))∗∗,需要直接输出运算表达式的运算结果时,可以在‘(())‘前加‘((2+1))**,需要直接输出运算表达式的运算结果时,可以在 `(())` 前加 `((2+1))∗∗,需要直接输出运算表达式的运算结果时,可以在‘(())‘前加‘` 符。
**示例1:**简单的数值计算。
[root@server ~]$ echo $((1+1))
2
[root@server ~]$ echo $((6*3))
18
[root@server ~]$ ((i=5))
[root@server ~]$ ((i=i*2))
[root@server ~]$ echo $i
10
**示例2:**复杂的数值计算。
[root@server ~]$ ((a=1+2**3-4%3))
[root@server ~]$ echo $a
8[root@server ~]$ b=$((a=1+2**3-4%3))
[root@server ~]$ echo $b
8
[root@server ~]$ echo $((a=1+2**3-4%3))
8[root@server ~]$ a=$((100*(100+1)/2))
[root@server ~]$ echo $a
5050
[root@server ~]$ echo $((100*(100+1)/2))
5050
**示例3:**特殊运算符号
[root@server ~]$ a=8;echo $((a+=1))
9
[root@server ~]$ echo $((a**2))
81
**示例4:**比较和判断
[root@server ~]$ ((3<8))
[root@server ~]$ echo $?
0
[root@server ~]$ echo $((3<8))
1[root@server ~]$ ((3>8))
[root@server ~]$ echo $?
1
[root@server ~]$ echo $((3>8))
0[root@server ~]$ echo $((3==3))
1[root@server ~]$ if ((8>7 && 5==5));then echo yes;fi
yes
**示例5:**变量前后使用–和++特殊运算符的表达式
[root@server ~]$ a=10
[root@server ~]$ echo $((a++))
10
[root@server ~]$ echo $a
11[root@server ~]$ echo $((--a))
10
[root@server ~]$ echo $a
10
总结:
- 执行
echo $((a++))
和echo $((a--))
命令输出整个表达式时,输出的值即为a
的值,表达式执行完毕后,会对a
进行++
、--
的运算 - 执行
echo $((++a))
和echo $((--a))
命令输出整个表达式时,会先对a
进行++
、--
的运算,然后再输出表达式的值,即为a运算后的值。
**示例6:**通过 (())运算后赋值给变量
[root@server ~]$ num=99
[root@server ~]$ echo $((num+1))
100
[root@server ~]$ num=$((num+1))
[root@server ~]$ echo $num
100
3 let 命令
let运算命令的语法格式为:let 表达式
let表达式的功能等同于:((表达式))
示例:
[root@server ~]$ i=2
[root@server ~]$ i=i+8
[root@server ~]$ echo $i
i+8[root@server ~]$ i=2
[root@server ~]$ let i=i+8
[root@server ~]$ echo $i
10
4 expr 命令(进阶)
expr(evaluate(求值)expressions(表达式))
命令既可以用于整数运算,也可以用于相关字符串长度、匹配等的运算处理。
基本用法示例
[root@server ~]$ expr 2+2
2+2
[root@server ~]$ expr 2 + 2
4[root@server ~]$ expr 2 * 2
expr: syntax error
[root@server ~]$ expr 2 \* 2
4
在使用 expr
时:
- 运算符及用于计算的数字左右都至少有一个空格,否则会报错。
- 使用乘号时,必须用反斜线屏蔽其特定含义,因为 Shell 可能会误解星号的含义。
5 bc 命令
bc
是UNIX/Linux下的计算器,因此,除了可以作为计算器来使用,还可以作为命令行计算工具使用。可用于小数计算
示例:
[root@server ~]$ 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+3*4-6/3^3%4
13
scale=4
1/3
.3333
quit[root@server ~]$ echo '1+3*4-6/3^3%4' | bc
13
[root@server ~]$ echo 'scale=4;1/3' | bc
.3333
6 awk 命令实现计算
利用awk进行运算的效果也很好,适合小数和整数,特别是命令行计算,尤其是小数,运算很精确,好用。
[root@server ~]$ echo 7.7 3.8 | awk '{ print $1-$2 }'
3.9
[root@server ~]$ echo 358 113 | awk '{ print ($1-3)/$2 }'
3.14159
7 $[] 符号的运算
示例1:
[root@server ~]$ echo $[1+1]
2
[root@server ~]$ echo $[4-2]
2
[root@server ~]$ echo $[2*2]
4
[root@server ~]$ echo $[4/2]
2
[root@server ~]$ echo $[5/2]
2
[root@server ~]$ echo $[5%2]
1
[root@server ~]$ count=3;echo $[(count+1)*3]
12
[root@server ~]$ count=3;echo $[ ++count + 3 ]
7
[root@server ~]$ count=3;echo $[ count++ + 3 ]
6
[root@server ~]$ count=3;echo $[ --count + 3 ]
5
[root@server ~]$ count=3;echo $[ count-- + 3 ]
6
**示例2:**通过一条命令计算输出 1+2+3+...+10
的表达式,并计算出结果,请使用bc命令计算。输出内容如1+2+3+4+5+6+7+8+9+10=55
。
[root@server ~]$ echo $(seq -s + 10) | bc[root@server ~]$ echo $(( $(echo {1..10} | tr ' ' '+' ) ))[root@server ~]$ echo $[ $(echo {1..10} | tr ' ' '+' ) ]
erver ~]$ echo [4/2]2[root@server][4/2] 2 [root@server ~][4/2]2[root@server ] echo [5/2]2[root@server][5/2] 2 [root@server ~][5/2]2[root@server ] echo [51[root@server][5%2] 1 [root@server ~][51[root@server ] count=3;echo [(count+1)∗3]12[root@server][(count+1)*3] 12 [root@server ~][(count+1)∗3]12[root@server ] count=3;echo [++count+3]7[root@server][ ++count + 3 ] 7 [root@server ~][++count+3]7[root@server ] count=3;echo [count+++3]6[root@server][ count++ + 3 ] 6 [root@server ~][count+++3]6[root@server ] count=3;echo [−−count+3]5[root@server][ --count + 3 ] 5 [root@server ~][−−count+3]5[root@server ] count=3;echo $[ count-- + 3 ]
6
**示例2:**通过一条命令计算输出 `1+2+3+...+10` 的表达式,并计算出结果,请使用bc命令计算。输出内容如`1+2+3+4+5+6+7+8+9+10=55`。```bash
[root@server ~]$ echo $(seq -s + 10) | bc[root@server ~]$ echo $(( $(echo {1..10} | tr ' ' '+' ) ))[root@server ~]$ echo $[ $(echo {1..10} | tr ' ' '+' ) ]