Shell脚本

Shell脚本

文章目录

  • Shell脚本
    • 1. Shell脚本的运行
      • 1.在新线程中运行
      • 2.在当前线程执行脚本
    • 2.变量
      • 1.变量初识
      • 2.变量的作用域
      • 3.Shell命令的替换
      • 4.位置参数(命令行参数)
    • 3.字符串
    • 4.数组
      • 1.数组的定义
      • 2.数组的合并
      • 3.数组元素删除
    • 5.内置命令
      • 1.内建命令
      • 2.read命令详解
      • 3.declare命令详解
    • 6.数学运算
      • 1.数学计算
    • 7.选择结构
      • 1.if语句
      • 2.if else语句
      • 3.if elif else语句
      • 4.test命令
      • 5.[[]]关键字
      • 6.case in语句
    • 8.循环结构
      • 1.while循环
      • 2.until循环
      • 3.for循环
      • 4.select in循环
      • 5.break和continue
    • 9.函数
      • 1.函数基础
      • 2.函数参数
    • 10.补充

1. Shell脚本的运行

1.在新线程中运行

一劳永逸

给shell脚本赋予执行权限,然后直接执行

[root@cuizja learnlinux]# chmod +rwx readtest.sh
[root@cuizja learnlinux]# ./readtest.sh 
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]# 

动态选择

每次执行动态的赋予脚本类型

[root@cuizja learnlinux]# /bin/bash readtest.sh 
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]# 

通过bash命令运行,bash命令中指定了bash的路径

[root@cuizja learnlinux]# bash readtest.sh 
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]# 

2.在当前线程执行脚本

  • ‘source filename’
[root@cuizja learnlinux]# source readtest.sh 
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]# 
  • . filenname
[root@cuizja learnlinux]# . readtest.sh 
What is your name?
ccc
hello,ccc
[root@cuizja learnlinux]# 

2.变量

1.变量初识

变量定义的三种方式

  • 变量名=值
  • 变量名='值'
  • 变量名="值"

**注意:**这里的赋值号=两侧不能有空格

如果值中没有空白字符(空格,tab缩进等),就可以不用引号

变量命名规范

  • 由数字,字母和下划线组成
  • 必须是字符或下划线开头,不能数字开头
  • 不能使用shell关键字

变量的调用

变量使用$符号调用,但是有的时候需要和字符串共同输出是可以加{}区分变量和字符串的边界,例如

name='ccc'
echo "I am ${name}ccc"

这里通过{}区分调用的是name变量而不是nameccc.

这里推荐调用变量的时候加上{},这是个良好的编程习惯

变量的修改

变量的修改和变量的定义是相同的语句,不必在变量前加$

单引号和双引号的区别

单引号中变量进行原样输出,即使遇到${}也不会解析成变量调用语句

双引号中的变量会对变量中的调用进行解析,然后输出

[root@cuizja learnlinux]# vim doubletest.sh
#!/bin/bash
name=abc
echo 'name=${name}'
echo "name=${name}"

[root@cuizja learnlinux]# ./doubletest.sh 
name=${name}
name=abc

建议数字不加引号,如果必须原样输出就加单引号,否则一律添加双引号

将命令结果赋值给变量

shell也支持将命令的执行结果赋值给变量,常见有以下两种写法

  • 变量名=`执行命令`
  • 变量名=$(执行命令)

这两种方式能够达到相同的效果,但是一般推荐使用$(),因为在编辑器中反引号容易被人看错成单引号

[root@cuizja learnlinux]# vim testfanyinhao.sh
#!/bin/bash
echo $(ls /usr/learnlinux)
echo `ls /usr/learnlinux`

[root@cuizja learnlinux]# ./testfanyinhao.sh 
doubletest.sh hello.sh helloworld.sh readtest.sh testfanyinhao.sh test.log
doubletest.sh hello.sh helloworld.sh readtest.sh testfanyinhao.sh test.log

只读变量

使用readonly命令可以将变量定义为只读变量,只读变量不能被修改

删除变量

使用unset命令删除变量,但是不能删除只读变量

2.变量的作用域

变量的作用域有三种:

  • 局部变量,只能在函数内部使用,使用local标识的变量时局部变量
  • 全局变量,可以在当前shell进程中使用
  • 环境变量,可以在多个shell进程中使用

注意:Shell中默认使用全局变量,如果变量前边没有标志,即使在函数内部定义的变量也是全局变量

环境变量

全局变量只在当前Shell进程中有效,对其他Shell进程和子进程无效,可以使用export命令将全局变量导出,变量就变成环境变量了.

**注意:**两个没有父子关系的Shell进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即"传子不传父"

最简单的创建Shell子进程的方式是运行bash命令:

[root@cuizja learnlinux]# echo $$    #输出当前进程的PID
26746
[root@cuizja learnlinux]# bash       #创建子进程
[root@cuizja learnlinux]# echo $$    #输出子进程PID
26906
[root@cuizja learnlinux]# exit       #返回父进程
exit
[root@cuizja learnlinux]# echo $$    #输出进程PID
26746

下面演示一下环境变量的使用

[root@cuizja learnlinux]# a=22       #定义变量
[root@cuizja learnlinux]# echo $a    #当前进程可以查看
22
[root@cuizja learnlinux]# bash       #进入子进程
[root@cuizja learnlinux]# echo $a    #子进程无法查看变量

[root@cuizja learnlinux]# exit       #退出子进程,返回父进程
exit
[root@cuizja learnlinux]# export a   #将父进程的全局变量转换为子进程的环境变量
[root@cuizja learnlinux]# bash       #进入子进程
[root@cuizja learnlinux]# echo $a    #子进程可以查看变量
22

如果想要在定义变量的同时导出为环境变量,可以写作export a=22

注意

通过export导出的环境变量只对当前进程及其子进程有效,而且父进程关闭,父进程的环境变量也失效了,所以说这里的环境变量时临时的

如果说想要让一个变量在所有进程都有效,只有将变量写入到Shell配置文件中才可以,这样在shell进程每次将启动时都会执行配置文件中的代码,做初始化工作.

3.Shell命令的替换

命令替换的两种方式

  • 反引号
  • $()

可以是执行一个命令,也可以是多个命令,多个命令用;隔开

注意

如果命令执行的结果包含多行(换行符),或多个连续的空白符,那么在输出变量时一定要使用双引号""包围,否则系统会使用默认的空白符填充,这样会导致换行无效,以及连续的空白符被压缩成一个.

4.位置参数(命令行参数)

在运行脚本文件abc.sh时,我们可以给他们传递一些参数,这些参数在脚本文件内部使用$n的形式接受,例如$1表示第一个参数,依次类推

这里的通过$n的方式接受的参数就叫做位置参数

如果参数个数太多,达到或者超过10个,就需要用这个方式接受${n},{}就是为了区分参数边界

给脚本文件传递位置参数

[root@cuizja learnlinux]# vim locationtest.sh
#!/bin/bash
echo $1
echo $2
[root@cuizja learnlinux]# chmod +rwx locationtest.sh 
[root@cuizja learnlinux]# ./locationtest.sh var1 var2
var1
var2
[root@cuizja learnlinux]# 

给函数传递位置参数

[root@cuizja learnlinux]# vim funcvartest.sh
#!/bin/bash
function func(){
        echo "$1"
        echo "$2"
}
func var1 var2

[root@cuizja learnlinux]# chmod +rwx funcvartest.sh 
[root@cuizja learnlinux]# ./funcvartest.sh 
var1
var2

特殊变量

  • $0 当前脚本的文件名
  • $n(n>0) 传递脚本或函数的参数,n表示第几个参数
  • $# 传递给脚本或函数的参数个数
  • $* 传递给脚本或函数的所有参数
  • $@ 传递给脚本或函数的所有参数
  • $? 上个命令的退出状态,或函数的返回值
  • $$ 当前Shell进程PID

$*$@的区别

  1. 如果没有被双引号包围,没有区别
  2. 当被双引号包围时
    • $*会将所有参数合并成一个参数
    • $@ 仍然将每个参数看做独立的参数,不会合并

比如传递5个参数,$*会合并5个参数,而$@仍然是相互独立的5个参数

[root@cuizja learnlinux]# vim vartest.sh
#!/bin/bash
echo '$*'
for var in "$*"
do
        echo "$var"
done
echo '$@'
for var in "$@"
do
        echo "$var"
done

[root@cuizja learnlinux]# chmod +rwx vartest.sh 
[root@cuizja learnlinux]# ./vartest.sh a b c d e
$*
a b c d e
$@
a
b
c
d
e

3.字符串

字符串长度

${#string_name}

字符串拼接:只需将两个字符串放在一起,即可拼接字符串

${str1}${str2}

字符串截取

格式 说明
${String: start :length} 从String字符串的左边第start个字符开始,向右截取length个字符
${String: start} 从String字符串的左边第start个字符开始截取,直到最后
${String:0-start:length} 从String字符串的右边第start个字符开始,向右截取length个字符
${String:0-start} 从String字符串的右边第start个字符开始截取,直到最后
${String#*chars} 从String字符串第一次出现chars的位置开始,截取chars右边的所有字符
${String##*chars} 从String字符串最后一次出现chars的位置开始,截取chars右边的所有字符
${String%chars*} 从String字符串第一次出现chars的位置开始,截取chars左边的所有字符
${String%%chars*} 从String字符串最后一次出现chars的位置开始,截取chars左边的所有字符
[root@cuizja learnlinux]# vim chartest.sh
#!/bin/bash
str=abcdefghijklmnopq
echo "${str:3:5}"
echo "${str:3}"
echo "${str:-3:2}"
echo "${str:-3}"
echo "${str#*de}"
echo "${str##*de}"
echo "${str%de*}"
echo "${str%%de*}"
[root@cuizja learnlinux]# ./chartest.sh 
defgh
defghijklmnopq
abcdefghijklmnopq
abcdefghijklmnopq
fghijklmnopq
fghijklmnopq
abc
abc

4.数组

1.数组的定义

Bash Shell只支持一维数组

数组定义的一般形式为:`array_name=(ele1,ele2,ele3,…)

Shell是弱类型的,数组中的元素类型不用必须相同

Shell数组的长度是不固定的,定义完成后可以直接增加元素

数组增加元素

直接array_name[index]=value就可以给数组增加元素

[root@cuizja learnlinux]# arr=(1 2 3)
[root@cuizja learnlinux]# echo ${arr[*]}
1 2 3
[root@cuizja learnlinux]# arr[3]=4
[root@cuizja learnlinux]# echo ${arr[*]}
1 2 3 4

数组部分赋值

array_name=([i]=value [j]=value ....)这样既可将数组对应的位置赋值,其他位置不赋值

获取数组元素

  • 一般形式:${array_name[index]}
  • 所有元素:${array_name[*]},${array_name[@]}

获取数组长度

数组长度的获取和字符串长度的获取相同,都是使用#获取

  • ${#array_name[@]}
  • ${#array_name[*]}

这里我简单的理解为${#变量名}就是求长度的运算

2.数组的合并

数组的合并就是讲两个数组连接成一个数组

**思路:**先利用@*将数组扩展成列表(扩列操作),然后再合并到一起

格式

  • array_new=(${array1[@]} ${array2[@]})
  • array_new=(${array1[*]} ${array2[*]})

3.数组元素删除

使用unset关键字删除数组元素

格式

  • unset array_name[index] 删除单个元素
  • unset array_name 删除整个数组

5.内置命令

1.内建命令

Shell内建命令,就是由Bash自身提供的命令,而不是文件系统中的某个可执行文件

可以使用type来确定一个命令是否为内建命令

[root@cuizja learnlinux]# type cd
cd is a shell builtin
[root@cuizja learnlinux]# type ifconfig
ifconfig is /usr/sbin/ifconfig

Bash Shell中内建命令列表

命令 说明
: 扩展参数列表,执行重定向操作
. 读取并执行指定文件中的命令(在当前shell环境中)
alias 为命令定义一个别名
bg 将文件以后台模式运行
bind 将键盘序列绑定到一个readline函数或宏
break 退出for,while,select或until循环
builtin 执行指定的shell内建命令
caller 返回子函数调用的上下文
cd 切换文件目录
command 执行命令,无需进行通常的shell查找
compgen 为指定单词生成可能的补全匹配
complete 显示指定的单词是如何补全的
compopt 修复指定单词的补全选项
continue 继续执行for,while,select或until下一次循环
declare 声明一个变量或变量类型
dirs 显示当前存储目录的列表
disown 从进程作业表中删除指定的作业
echo 输出内容到STDOUT
enable 启用或禁用指定的内建shell命令
eval 将指定的参数拼接成一个命令,然后执行该命令
exec 用指定命令替换shell进程
exit 强制shell以指定的状态码退出
export 设置子shell进程可用的变量
fc 从历史记录中选择命令列表
fg 将作业以前台模式运行
getopts 分析指定的位置参数
hash 查找并记住指定命令的全路径名
help 显示帮助文件
history 显示命令历史记录
jobs 列出活动作业
kill 想指定的进程ID发送一个系统信号
let 计算一个数学表达式中的每个参数
local 创建局部变量
logout 退出登录shell
mapfile 从STDID读取数据行,并将其假如索引数组
popd 从目录栈中删除记录
printf 使用格式化字符串显示文本
pushd 向目录栈中添加一个目录
pwd 显示当前工作目录的路径名
read 从STDIN读取一行数据并将其赋值给一个变量
readarray 从STDIN读取数据行并将其放入索引数组
readonly 只读变量
return 强制函数以某个值退出,这个值可以被调用的脚本获取
set 设置并显示环境变量的值和shell属性
shift 将位置参数依次下降一个位置
shopt 打开/关闭控制shell可选行为的变量值
source 读取并执行指定文件中的命令
suspend 暂停shell的执行,知道收到一个SIGCOUT信号
test 基于指定条件返回退出状态码0或1
times 显示累计的用户和系统时间
trap 如果收到了指定的系统信号,执行指定的命令
type 显示命令类型
typeset 声明一个变量或变量类型
ulimit 为系统用户设置指定的资源的上限
umask 为新建的文件和目录设置默认权限
unalias 删除指定的别名
unset 删除环境变量或shell属性
wait 等待指定的进程完成,并返回退出状态码

2.read命令详解

格式

read [-options] [variables]

选项

  • -a array 把读取的数据赋值给数组array,下标从0开始
  • -d key 用字符串key指定读取结束位置,不包含key
  • -e 输入的同时识别转义符
  • -n num 读取num个字符
  • -p prompt 显示提示信息,提示内容为prompt
  • -r 原样读取,不识别转义字符
  • -s 静默模式,输入的内容不会显示在界面
  • -t seconds 设置超时时间,超时后返回一个非0的退出状态,表示读取失败
  • -u fd 使用文件描述符fd作为输入源,而非标准输入,类似于重定向

3.declare命令详解

格式

declare +/- [选项] [变量名=变量值]

-表示设置属性,+表示取消属性

选项

  • -f name 列出之前由用户在脚本中定义的函数名和函数体
  • -F name 仅列出自定义函数名称
  • -g name 在shell函数内部创建全局变量
  • -p name 显示指定变量的属性和值
  • -a name 声明变量为普通数组
  • -A name 生命变量为关联数组(支持索引下标为字符串)
  • -i name 将变量定义为整数型
  • -r name 将变量定义为只读,等价于readonly

6.数学运算

1.数学计算

Shell中的运算符和其他编程语言基本相同,但是和其他语言不同的是,Shell编程进行数学计算必须使用数学计算命令,下边列举一些数学计算的命令

数学计算命令

运算操作符/运算命令 说明
(()) 用于整数运算,效率很高
let 用于整数运算,和(())相似
$[] 用于整数运算,不如(())灵活
expr 可用于整数运算,也可以处理字符串,比较麻烦,需要注意各种细节
bc Linux下的计算器程序,可以处理整数和小数,Shell只支持整数运算,如果想要计算小数,必须使用bc这个外部计算器
declare -i 将变量定义为整数,然后就可以直接进行基础的数学运算(仅支持最基本的数学运算,加减乘除取余等)

7.选择结构

1.if语句

格式

if condition
then
	statements
fi

condition是判断条件,条件成立,执行then后边的语句,否则直接结束判断

注意,这里必须使用fi结束if语句

也可以将if和then写在一行

if condition; then
	statements
fi

注意,当if和then写在一行的时候,condition后边的分号;不能省略

2.if else语句

格式

if condition
then 
	statement1
else
	statement2
fi

如果condition条件成立,执行then后边,否则执行else后边

3.if elif else语句

类似于java中的if - else if - else结构

格式

if condition1
then
	statement1
elif condition2
then
	statement2
else
	statement2
fi

注意,if和elif后边都必须有then

4.test命令

test命令有很多选项,可以进行数值,字符串和文件三个方面的检测

格式

test expression

表达式成立,退出状态为0,否则为非0

可以简写为[]

[ expression ]

在expression前后需要有空格,并且空格是必须的

test命令的选项

  1. 与文件相关
    • -b filename 判断文件是否存在,并且是否为块设备文件
    • -c filename 判断文件是否存在,并且是否为字符设备文件
    • -d filename 判断文件是否存在,并且是否为目录文件(是否是文件夹)
    • -e filename 判断文件是否存在
    • -f filename 判断文件是否存在,并且是否为普通文件(文件)
    • -L filename 判断文件是否存在,并且是否为符号链接文件
    • -p filename 判断文件是否存在,并且是否为管道文件
    • -s filename 判断文件是否存在,并且是否为非空
    • -S filename 判断文件是否存在,并且是否为套接字文件
    • -r filename 判断文件是否存在,并且是否拥有读权限
    • -w filename 判断文件是否存在,并且是否拥有写权限
    • -x filename 判断文件是否存在,并且是否拥有执行权限
    • -u filename 判断文件是否存在,并且是否拥有SUID权限
    • -g filename 判断文件是否存在,并且是否拥有SGID权限
    • -k filename 判断文件是否存在,并且是否拥有SBIT权限
    • filename1 -nt filename2 判断文件1的修改时间是否比文件2的新new
    • filename1 -ot filename2 判断文件1的修改时间是否比文件2的旧old
    • filename1 -ef filename2 判断文件1和文件2是否同一个(本质上比较文件inode是否相等)
  2. 数值相关
    • num1 -eq num2 判断1和2是否相等
    • num1 -ne num2 判断1和2是否不等
    • num1 -gt num2 判断1是否大于2
    • num1 -lt num2 判断2是否小于2
    • num1 -ge num2 判断1是否大于等于2
    • num1 -le num2 判断1是否小于等于2
  3. 字符串相关
    • -z str 判断str是否为空
    • -n str 判断str是否非空
    • str1=str2 str1==str2 ===是等价的,都用来判断字符串是否相等
    • str1 != str2 判断是否不等
    • str1\>str2 判断1是否大于2,\是转义字符,防止>被当做重定向运算符
    • str1\ 判断1是否小于2
  4. 逻辑运算相关
    • expression1 -a expression2 逻辑与,同时成立
    • expression1 -o expression2 逻辑或,有一个成立就成立
    • !expression 逻辑非,取反

注意 Shell中不支持>= 和<=运算符

5.[[]]关键字

[[]]和test类似,test能做到的[[]]也能做到,test不能做到,[[]]也能做到,可以把他当做test的升级版

格式

[[ exptression ]]

注意,expression前后需要加空格

与test的区别

  • 变量名不需要用双引号""包围

  • 不能对>,<进行转义,也不需要转义

  • 支持&&,||,!等逻辑运算符

  • 支持正则表达式判断

    [[ str =~ regex ]]

[[]]更强大,建议用它替代test

6.case in语句

格式

case expression in
	pattern1)
		statement1
		;;
	pattern2)
		statement2
		;;
	*)
		statement
esac

expression 可以是一个变量,数字,字符串,命令执行结果

pattern可以是一个数字,字符串甚至简单的正则表达式,正则表达式用[regex]

8.循环结构

1.while循环

格式

while condition
do
	statements
done

执行流程

先condition判断,成立进入循环,每次执行完statement之后,重新判断condition,不成立时结束.

2.until循环

与while循环恰恰相反,不成立时进行循环,一旦成立终止循环

格式

until condition
do
	statements
done

执行顺序

先condition判断,不成立进入循环,每次执行完statement之后,重新判断condition,成立时结束.

3.for循环

C语言风格的for循环

for((exp1;exp2;exp3))
do
	statements
done

执行过程

1.先执行exp1

2.在执行exp2,如果判断结果成立,这执行statement,否则结束循环

3.执行完循环体再执行exp3

4.重复执行2,3步骤,直到2的结果不成立

Python风格的for in 循环

格式

for variable in value_list
do 
	statements
done

相当于java中的增强for循环

value_list的书写形式

  1. 直接给出具体的值,多个值用空格分开,例如1 2 3 4,"abc" "def" "ghi"
  2. 给出一个取值范围{start..end},例如{1..100}
  3. 使用反引号``或者$()命令的执行结果
  4. 使用shell通配符,例如*.sh
  5. 使用特殊变量,例如$#,$*,$@,$?,$$

4.select in循环

与for in 循环类似,但是他可以将value_list的内容输出,供用户选择

格式

select variable in value_list
do
	statements
done

ctrl+D结束循环

5.break和continue

break格式

break n

n表示循环成熟,省略n表示跳出当前循环

continue格式

continue n

n表示循环层数,省略表示跳过本次循环,直接进入下一次循环

如果n有值,表示内层循环直接跳过本次循环,外层循环也跳过本次循环

9.函数

1.函数基础

语法格式

function funcname(){
	statements
	[return value]
}

function 定义函数关键字

funcname 函数名

statement 函数中要执行的代码

return value 返回值

也可以简化为这样

funcname(){
	statements
	[return value]
}

function funcname{
	statements
	[return value]
}

函数调用

  • 不传参数 funcname
  • 需要传参数 funcname param1 param2 param3

2.函数参数

函数参数接收方式

  • $n 使用位置参数接收
  • $# 可以获取参数个数
  • $@或者$*可以一次性获取所有参数

10.补充

在之前的文章中的管道内容的补充

管道中的常用命令

命令 说明
awk 用于文本处理的解释性程序设计语言,通常被用作数据提取和报告的工具
cut 用于将每个输入文件(如果没有指定文件则为标准输入)的每行的指定部分输出到标准输出
grep 用于搜索一个或多个文件中指定模式的行
tar 用于归档文件的应用程序
head 用于读取文件的开头部分(默认是10行),如果没有指定未见,则从标准输入读取
paste 用于合并文件的行
sed 用于过滤和转换文本的流编辑器
sort 用于对文本文件的行进行排序
split 用于将文件分割成块
strings 用于打印文件中可打印的字符串
tac 与cat命令功能相反,用于倒序的显示文件或链接文件
tail 用于显示文件的结尾部分
tee 用于从标准输入读取内容写入到标准输出和文件
tr 用于转换或删除字符
uniq 用于报告或忽略重复的行
wc 用于打印文件中的总行数,单词或字节数

你可能感兴趣的:(学习,shell,linux,笔记)