目录
Shell 脚本就是把一批命令集合在一起,一起解决一些复杂的问题。Shell 中有变量、循环、函数等内容,类似于程序设计,但本质上不是程序设计。
Window 系统中的 Shell —— PowerShell。
0、起步前的一些内容
0-1)给命令设置别名并使用(alias)
alias 别名="原来的命令"
可以简化使用频率较多的、长度较长的命令的使用。
0-2)在一行中用 “ ; ” 来分割不同的命令
0-3)直接创建变量、使用变量
read myname | 给变量 myname 赋值 |
read myname password | 给两个变量同时赋值,以空格区分 |
echo $myname | 输出变量的值 |
username='zhangsan' | 直接创建变量并赋值(等号两侧不能有空格) |
testname='张三' readonly testname | 声明只读的变量,只读变量不能改变值 |
注意,在给变量赋值的命令中,如果等号两侧有空格,则会处理失败。如下图,如果等号左右两边都有空格,则会把第一个 username 作为命令,= 作为命令的第一个参数,'abc' 作为第二个参数来进行处理。
0-4)关于环境变量
环境变量是指当前操作系统中预定好的一批全局变量,可以在任意位置引用。
在 Linux 和 Window 系统中都有环境变量。
使用频率最高的环境变量是 PATH,PATH 中定义了一批文件夹(路径),表示只要是在这个目录下的命令,就可以在命令行中直接执行,而不需要输入完整的路径。
set | 设置当前shell的变量,包括当前用户的变量 |
env | 显示当前用户的变量 |
export | 显示当前导出成用户变量的shell变量 |
export PATH=$PATH:/opt | 修改PATH环境变量为原来的值加上一个/opt目录,即把/opt目录添加进环境变量PATH中 |
cat /etc/profile | 全局的环境变量,对任何的用户都生效 |
cd ~;cat .bash_history | 当前用户主目录下的环境变量,仅对当前用户生效(本地变量) |
echo $PATH | 输出环境变量PATH的内容 |
unset NAME | 删除一个环境变量 |
echo $USER | 当前登录的用户名 |
echo $UID | 当前用户的ID |
echo $HOME | 当前用户的主目录 |
echo $PS1 | |
export PS1="Hello \h ## " | 临时的重新配置,重新连接后会重置会原样 |
export PS1="[\u@\h \W]\\$ " | 恢复原样 |
0-5)关于 echo
echo -e 'Hello\nworld' | -e 是输出时支持转义符(在这里是换行) |
echo 'Hello\nWorld' | 如果没有-e则会将转义符一起输出 |
echo -e '\a' | 蜂鸣(警告时可用使用) |
echo -e 'helloo\bworld' | \b 是退格,输出 helloworld |
0-6)expr 进行比较和整数计算等
exper 可以处理整数的计算,浮点数不能参与其中
对数值或字符串进行比较,注意空格。
匹配字符串、查找字符串中是否包含字符、计算字符串的长度等
计算字符串的长度
计算匹配字符串的长度,可以看见是从头开始匹配的,返回的是匹配的字符个数。
计算两个字符串是否相等
查找字符串中是否含有某个字符,返回第一次出现的下标
0-7)使用 | bc 可以处理浮点运算
echo 输出 123.45 * 2.0 然后通过管道符交给 bc 进行处理,bc 是一个任意精度的计算器语言 。
1、编写一个最简单的 Shell 脚本
Shell 脚本中的每一行都是一个可以独立运行的命令。
编写一个脚本 shell-01.sh ,其中的内容如下:
vi shell-01.sh | 创建一个shell 脚本文件并写入内容 |
sh shell-01.sh | 执行 shell 脚本,于bash xxx相同 |
source shell-01.sh | 执行 shell 脚本(推荐使用) |
bash shell-01.sh | 使用bash壳程序来执行shell脚本(推荐使用) |
. shell-01.sh | 执行shell脚本 |
./shell-01.sh | 执行shell脚本,使用前需要给user添加该文件的执行权限(chmod u+x shell-01.sh) |
执行该脚本
2、Shell 脚本 接收参数
shell 脚本中接收的参数使用 ${1}、${2}·····${9} (或 $1、$2···)进行表示,$# 统计一共接收到多少个参数。
注意,如果使用 $x 的形式,则最多只能接受 9 个参数;而使用 ${x}的形式,则能接受到很多个。
如果:$10,本意想接收第十个参数,实际上输出的是第一个参数拼接上 0 的结果。
执行结果:
3、Shell 中三个引号的区别
三个引号分别为单引号(''),双引号(""),反引号(``).
单引号 '' | 单引号里的内容原样输出 |
双引号 "" | 双引号里面的变量能够正常的被替换 |
反引号 `` | 会执行引号内中的命令 可以使用反引号执行一个命令,并把输出的结果赋值给一个变量 now=`date` |
4、几个括号的使用
(()) | 用于重定义变量的值,可以代替 expr;也可以用作 for 循环 |
$(()) | 用于整数运算,里面的变量可加 $ 开头也可以不加 |
${} | 可以取特定参数或变量的值,如 ${1} 、${username} |
$() | 可以代替反引号,执行圆括号里面的命令。称为命令替换,它允许你将一个命令的输出结果赋值给一个变量或在命令行中直接使用 |
$[] | 用于整数运算,可以代替 expr |
参考:https://www.cnblogs.com/chengd/p/7803664.html
5、几个特殊的变量
$# | 传递到脚本的参数个数 |
$0 | 脚本的名称 |
$* | 以一个单字符串的形式显示所有向脚本传递的参数,输出时的参数个数可以超过9个 |
$$ | 脚本运行的当前进程id号 |
$! | 后台运行的最后一个进程的进程id号 |
$@ | 显示所有向脚本传递的参数,每一个参数都是一个单独的字符串 |
$? | 显示最后命令的退出状态,0表示正确,其他任何值表示错误;可以在脚本中自定义0-255的退出状态码 |
$_ | 上一个命令的最后一个参数 |
参考:Bash Shell $*, $@, $#, $$, $!, $?, $- 和 $_ - 知乎
6、顺序控制语句
# if后面跟着的是一条可执行命令,判断的依据就是该命令是否执行成功,返回 1
if 条件
then
命令
fi
## 或者 ##
if 条件; then
命令
fi
####
if 条件
then
命令
else
命令
fi
以下的一个 shell 程序,验证用户输入的参数是否为 hello。
if [[ $# -ne 1 ]]
then
echo "请输入一个参数"
return 2
fi
echo "continue"
sourceLength=`expr length 'hello'`
paramsLength=`expr length $1`
if [[ $sourceLength -eq $paramsLength ]];then
echo "equal"
else
echo "no"
fi
多重分支结构
# 多重分支结构,计算第一个参数加上 第二个参数与第三个参数的积 的和
# 先判断有没有三个参数,如果满足再判断前三个参数是不是都是数字
if [ $# -lt 3 ]; then
echo '请输入3个数字参数'
#exit 100 # 自己定义退出码,会退出连接状态,需要重新连接
return 100
elif [[ ${1} =~ ^[0-9]+$ && ${2} =~ ^[0-9]+$ && ${3} =~ ^[0-9]+$ ]]; then
echo "$1 + $2 * $3" | bc
else
echo "输入的内容不合法"
fi
case 的匹配结构
num=$1
case $num in
1)
echo '接收到数字 1'
;;
2)
echo '接收到数字 2'
;;
3)
echo '接收到数字 3'
;;
*)
echo '接收到其他内容'
esac
7、循环结构
计算 1+2+3+4+5 的和
7-1)for 循环
sum=0
for i in 1 2 3 4 5;
do
let sum+=i # 即 let sum=sum+i
done
echo $sum
sum=0
for i in 1 2 3 4 5;
# 也可以写成 for i in {1..5};
# 也可以写成 for ((i=0 ; i<=100 ; i+=1));
do
((sum+=i)) # 双括号表达式
# 其他的写法
# 1)
# sum=`expr $num + $i`
# 2)
# sum=`echo "$num + $i" | bc`
done
echo $sum
使用 break 退出当前一层的循环
7-2)while 循环
i=1
sum=0
while [[ $i -le 5 ]]
do
((sum+=i))
((i++))
done
echo $sum
8、函数
# 定义一个函数
function add(){
let sum=$1+$2 # 这里的 $1 和 $2 是传给函数的两个参数
echo $sum # echo相当于程序设计中的return,设置返回的内容
}
# 调用add函数 并传入参数
# add 123 11 # 传入自定义的参数
# add ${1} ${2} # 使用的时候传入参数
# 让函数的返回值赋值给一个变量
# result=`add 123 123`
result=$(add 123 $2) # 等价于反引号
echo "result 的结果是:$result"
使用函数来计算阶乘
res=1
function jie_cheng(){
if [[ $1 -gt 1 ]];then
res=$(($1 * res))
echo `jie_cheng $(( $1 - 1 ))` # 递归调用
else
echo $res
fi
}
jie_cheng ${1} # 调用函数,并传入程序接收到的第一个参数
9、数组
# 定义数组
# declare -a seasons
# 依次给数组中的元素赋值
# seasons[0]="Spring" # 如果字符串中有空格,则需要使用单引号
# seasons[1]="Summer"
# seasons[2]="Autumn"
# seasons[3]="Winter"
# 数组取值
# echo ${seasons[@]} # 输出数组中的全部内容
# echo ${seasons[1]} # 取数组的特定值
# 遍历数组
# for season in ${seasons[@]}; do
# echo ${season}
# done
# 修改数组中的指定值
#seasons[1]="None"
# echo ${seasons[@]}
# 取数组的长度
# echo "当前数组的长度:${#seasons[@]}"
# let count=${#seasons[@]}-1
# for ((i=0;i<=count;i++)); do
# echo "$i ${seasons[$i]}"
# done
# 另一种定义数组的方式
seasons=("Spring" "Summer" "Autumn" "Winter")
for ((i=0;i<${#seasons[@]};i++));do
echo "$i: ${seasons[$i]}"
done
10、字符串处理
10-1)字符串截取的方式 ${}
按特征(目标字符串)进行截取
定义字符串变量 url 如下:
# '#' 符号用于删除匹配字符串 左侧 的内容,只显示匹配字符串 右侧 的内容
# '%' 符号用于删除匹配字符串 右侧 的内容,只显示匹配字符串 左侧 的内容
# 对于 '#' ,通配符 '*' 位于要匹配的字符串的左侧,而对于 '%' ,则是右侧
echo ${url#*//}
# '#' 从左往右 开始匹配,匹配 第一次 出现的目标字符串 ''//
# '*//' 表示删除掉包括 '//' 在内左边的所有内容
echo ${url##*js}
# '##' 从左往右开始匹配,匹配 最后一次 出现的目标字符串 'js'
# '*js'表示替换掉包括 'js' 在内其左边的所有内容
echo ${url%//*}
# '%' 从左往右开始匹配,匹配 第一次 出现的目标字符串 '//',
# 然后删除 包括目标字符串在内的 右侧所有内容
echo ${url%%.*}
# '%%' 从左往右开始匹配,匹配 最后一次 出现的目标字符串 '.',
# 然后删除 包括目标字符串在内的 右侧所有的内容
按位置进行截取(并指定截取的长度)
# echo ${变量名:开始下标:要截取的长度}
echo ${num:2:5}
# 从下标为 2 的字符到下标为 2+5-1=6 的字符 输出的字符串的长度是 5
echo ${num:0-1:3}
# 从下标为 0-1 即 -1 即从最后一个字符开始往后截取(包括),要截取的长度是 3
echo ${num:5}
# 从下标为 5 的字符往后截取(包括),没有指定要截取的长度,则截取到最后
echo ${#num}
# 输出变量 num 的长度
10-2)AWK
是一种样式扫描与处理的工具,功能强于 sed 和 grep。
对字符串(变量)的扫描操作
echo $info | awk '{print $1}'
# awk 接收到变量 info 的内容,将按空格进行分隔,形成列
# $1 表示提取出第一列的内容 ($0 则原样输出)
# print 表示对提取出来的内容进行输出
awk 实际上是 行 列 处理逻辑(二维表):
指定根据何种符号进行分隔:
echo $url | awk -F '.' '{print $1}'
# -F 选项用于指定输入字段的分隔符
# '-F .' 指定根据 '.' 来进行分隔
指定多个分隔符来进行分隔:
length=`echo $url | awk -F '[/.]' '{print NF}'`
# '[/.]' 指定使用 / 和 . 进行分隔
# print NF 可以输出分割后的列数(分割成多少项)
for ((i=1;i<=length;i++));
do
echo $(echo $url | awk -F '[/.]' -v var=$i '{print $var}')
done
对文件的内容进行扫描操作
根据正则表达式匹配符合要求的行:
awk -F ':' '$1~/.o./ {print $1}' /opt/passwd.txt
# 扫描 /opt/passwd.txt 文件,根据 ':' 来分割每一行
# '$1~/.o./ {print $1}' 表示,如果分割后的第一项中含有字符 'o' ,
# 则输出该行分割后的第一项内容
# '$1~/正则表达式/ {print $1}'
awk -F ':' '$0!~/(root|adm)/' /etc/passwd
# 打印不包括 root 或 adm 的行
统计文件中的行数:
awk -F ':' 'BEGIN {sum=0} {sum+=1} END {print sum}' /etc/passwd
# 统计文件中的行数
awk -F ':' 'BEGIN {sum=0} $0!~/root/ {sum+=1} END {print sum}' /etc/passwd
# $0!~/root/ 统计不包含 root 的行数
# 等价的命令: grep -v root /etc/passwd | wc -l
if-else 结构:
awk -F ':' '{if ($1~/.*root.*/) print $0; else print $1}' /etc/passwd
# if else 结构
10-3)sed
sed 可以根据脚本中的命令来处理文本文件
常用的三个选项:
-e 指定多个规则进行多点编辑
-n 显示处理后的结果
-i 永久将编辑的结果保存到指定的文件中
常用的六个操作:
(1)a:新增,将新的内容添加到指定行的下一行。 如 5a 在第六行添加新内容
(2)c:取代,将新的内容取代指定的内容(某行与某行之间的内容)
(3)d:删除。如 6d 则是删除第六行
(4)i:插入,将新的内容插入到指定行的前一行
(5)p:打印,将搜索的结果数据打印出来,通常与sed -n 一起运行
(6)s:取代,直接进行取代的工作,搭配正则表达式进行
a 新增操作:
将指定内容在指定行的下一行开始添加,指定行后面原本内容往后移动。
d 删除操作:
删除指定行的内容
i 插入操作:
将指定内容插入到指定行 ,原本的内容往后移动。
d 删除多行:
删除 5到7 行的内容
c 操作 替换多行:
将1到5行的内容替换成xxxxxxxx
因为没有使用 -i ,并不会将修改后的结果进行保存。
'1,$c xxx' 将第一行到最后一行的内容,替换成指定的内容xxx。
p 检索内容:
-n 选项用于显示处理后的结果,如果不使用 -n 选项,则会回显源文件的内容(回显经过操作后文件中的内容,源文件中的内容并未被修改)
d 操作 删除匹配的行:
搜索并且指定删除所有包含 root 的行
s 操作 替换:
sed -i 's/内容A/内容B/g' sed_01.txt
内容A 表示要匹配的目标字符串,内容B 表示替换成的内容, g 表示启用全局匹配,如果不使用g,则只会替换第一项匹配的内容。
e 操作 多点处理:
即同时做多个操作
全局替换,用 ????? 替换 root,并 删除第四行及以后的内容。