# 实战脚本简述

# 流程控制

# if

if [ 条件判断式 ];then
	程序
fi
if [ 条件判断式 ]
	then
		程序
fi
if [ 条件判断式 ]
	then
		条件成立时,执行的程序
	else
		条件不成立时,执行的另一个程序
fi
if [ 条件判断式1 ]
	then
		条件成立时,执行的程序
elif [ 条件判断式2 ]	
	then
		条件成立时,执行的程序

...省略更多条件...

else
		条件不成立时,执行的另一个程序
fi

# case

case $变量名 in
	"值1")
		如果变量的值等于值1,则执行程序1
		;;
	"值1")
		如果变量的值等于值2,则执行程序2
		;;

	…省略其他分支…

	*)
		如果变量的值都不是以上的值,则执行此程序
		;;
esac

# for

for 变量 in 值1 值2 值3 ...
	do 
		程序
	done
for (( 初始值;循环控制条件;变量变化 ))
	do
		程序
	done

# while

while [ 条件判断式 ]
    do
        程序
    done

# until

until [ 条件判断式 ]
	do
		程序
	done

# 特殊控制语句

exit [返回值]

break

continue

# 函数

function 函数名 () {
	程序
}

# 工具命令

# cut

# cut [选项]  文件名
常见选项:
    -c:	以字符为单位进行分割,截取
    -d:	自定义分隔符,默认为制表符\t
    -f:	与-d一起使用,提取第几列

[root@xiaoshaozi ~]# echo "zs=123=456=789" | cut -d "=" -f 1,2,4
zs=123=789

# wc

[root@localhost ~]# wc [选项] 文件名
选项:
    -l:只统计行数
    -w:只统计单词数
    -m:只统计字符数

# seq

seq: 是一个序列(squeue )的缩写,主要用来输出序列化的东西

seq [选项] 尾数						#从到尾数
seq [选项] 首数 尾数				#从首数到尾数
seq [选项] 首数 增量 尾数			#从首数以增量到尾数

以指定增量从首数开始打印数字到尾数。

常用选项:
    -f	使用printf 样式的浮点格式
    -s	使用指定字符串分隔数字(默认使用:\n)
    -w	在列前添加0 使得宽度相同【自动补位】

指定分割符

[root@server1 mnt]# seq -s '#' 5
1#2#3#4#5

[root@server1 mnt]# seq -s ' ' 10
1 2 3 4 5 6 7 8 9 10

[root@server1 mnt]# seq -w 10
01
02
03
04
05
06
07
08
09
10

产生范围数字

产生-2~10内的整数,增量为2:

[root@server1 mnt]# seq -2 2 10
-2
0
2
4
6
8
10


产生98~101之间的整数,并且要求输出数字宽度相同,不足的用0或空格补足:

[root@server1 mnt]# seq -f "%03g" 98 101
098
099
100
101
[root@server1 mnt]# seq -f "%3g" 98 101   
 98
 99
100
101

注意:通过%后添加0替代空格补足空位

# awk

# sed

# column

column对齐字段

常用选项:
    -t :表格,默认以空格间隔
    -s:需要配合-t使用,指定分隔符

获取的文档版式比较乱

cat tee.txt 
10.10.10.161 | SUCCESS | rc=0 | (stdout) 21
10.10.10.33 | SUCCESS | rc=0 | (stdout) 22
10.10.10.1 | SUCCESS | rc=0 | (stdout) 62

排版

cat tee.txt |column -t 
10.10.10.161  |  SUCCESS  |  rc=0  |  (stdout)  21
10.10.10.33   |  SUCCESS  |  rc=0  |  (stdout)  22
10.10.10.1    |  SUCCESS  |  rc=0  |  (stdout)  62

sort 多重排序 (opens new window)整理

$ cat tee.txt |column -t |sort  -k5r -k1
10.10.10.97   |  SUCCESS  |  rc=0  |  (stdout)  7
10.10.10.98   |  SUCCESS  |  rc=0  |  (stdout)  7
10.10.10.99   |  SUCCESS  |  rc=0  |  (stdout)  7
10.10.10.129  |  SUCCESS  |  rc=0  |  (stdout)  65
10.10.10.130  |  SUCCESS  |  rc=0  |  (stdout)  65
10.10.10.131  |  SUCCESS  |  rc=0  |  (stdout)  65
10.10.10.1    |  SUCCESS  |  rc=0  |  (stdout)  62
10.10.10.2    |  SUCCESS  |  rc=0  |  (stdout)  62

# expr

正确: 所有操作符的两边,都需要有空格。
[root@localhost scripts]# expr 1 + 1
2

"index string chars"用法示例。

[root@localhost ~]# expr index abcde dec   # 在 abcde 中搜索 d 在第几个位置
3

[root@localhost ~]# expr index abcde xdc   # 在 abcde 中搜索x没有,在搜索d
3

[root@localhost ~]# expr index abcde 1
0

"substr string pos len"用法示例。

该表达式是从string中取出从pos位置开始长度为len的子字符串。如果pos或len为非正整数时,将返回空字符串。

[root@localhost ~]# expr substr abcde 2 3
bcd
 
[root@localhost ~]#  expr substr abcde 2 4
bcde
 
[root@localhost ~]#  expr substr abcde 2 5
bcde
 
[root@localhost ~]#  expr substr abcde 2 0
 
[root@localhost ~]# expr substr abcde 2 -1

"length string"用法示例。该表达式是返回string的长度,其中string不允许为空,否则将报错,所以可以用来判断变量是否为空。

[root@xuexi ~]# expr length abcde
5
 
[root@xuexi ~]# expr length 111
3
 
[root@localhost scripts]# echo $xxx
 
[root@xuexi ~]# expr length $xxx
expr: syntax error

"+ token"用法示例。

expr中有些符号和关键字有特殊意义,如"match"、"index"、"length",如果要让其成为字符,使用该表达式将任意token强制解析为普通字符串。格式:expr index 特殊关键字

[root@localhost scripts]# expr index index d
expr: 语法错误
[root@localhost scripts]# expr index length g
expr: 语法错误
[root@localhost scripts]# expr index + length g
4

对值为关键字的变量来说,则无所谓。

[root@localhost scripts]# len=lenght
[root@localhost scripts]# expr index $len g
4

"string : REGEX"字符串匹配示例。要输出匹配到的字符串结果,需要使用",否则返回的将是匹配到的字符串数量。

[root@localhost ~]#expr abcde : 'ab\(.*\)'   # 'ab\(.*\)'  ab代表不匹配 .代表匹配一个  .*匹配所有  \是为了转移字符,转移(  和)
cde
 
[root@localhost ~]# expr abcde : 'ab\(.\)'
c
 
[root@localhost ~]# expr abcde : 'ab\(..\)'
cd
 
[root@localhost ~]# expr abcde : 'ab.*'    #  不写() 代表反馈匹配上的数字各个数
5
 
[root@localhost ~]# expr abcde : 'ab.'   
3
 
[root@localhost ~]# expr abcde : '.*cd*'
4
 
[root@localhost ~]# cat abc
abcdwe
[root@localhost ~]# expr `cat abc` : 'ab\(..\)'
cd

# grep

[root@localhost ~]# grep [选项] '关键字' 文件名
选项:
    -i:				忽略大小写
    -n:				输出行号
    -v:				反向查找 
    ^,$,?,*,[a-z],[x]正则符号
    ^$				匹配空行
	^[[:space:]]*#	匹配空行,匹配零个或多个空白字符,这包括空格、制表符和其他类型的空白字符
[root@localhost ~]# grep -n 'root' passwd
[root@localhost ~]# grep -ni 'root' passwd
[root@localhost ~]# grep '^root' passwd
[root@localhost ~]# grep 'root$' passwd
# grep -i root passwd						忽略大小写匹配包含root的行
# grep -w ftp passwd 						精确匹配ftp单词
# grep -w hello passwd 						精确匹配hello单词;自己添加包含hello的行到文件
# grep -wo ftp passwd 						打印匹配到的关键字ftp
# grep -n root passwd 						打印匹配到root关键字的行好
# grep -ni root passwd 						忽略大小写匹配统计包含关键字root的行
# grep -nic root passwd						忽略大小写匹配统计包含关键字root的行数
# grep -i ^root passwd 						忽略大小写匹配以root开头的行
# grep bash$ passwd 							匹配以bash结尾的行
# grep -n ^$ passwd 							匹配空行并打印行号
# grep ^# /etc/vsftpd/vsftpd.conf		匹配以#号开头的行
# grep -v ^# /etc/vsftpd/vsftpd.conf	匹配不以#号开头的行
# grep -A 5 mail passwd 				 	匹配包含mail关键字及其后5行
# grep -B 5 mail passwd 				 	匹配包含mail关键字及其前5行
# grep -C 5 mail passwd 					匹配包含mail关键字及其前后5行

# sort

[root@localhost ~]# sort [选项] 文件名
常用选项:
    -u :去除重复行
    -r :降序排列,默认是升序
    -n :以数字排序,默认是按字符排序
    -t :分隔符
    -k :第N列

# 符号

# 输出重定向

命令 &>/dev/null
命令 >/dev/null 2>&1

# 多命令

;	同时执行


&& 	$?=0,则命令2执行
	$?≠0,命令2不执行


||	$?≠0,则命令2执行
	$?=0,命令2不执行


[root@xiaoshaozi ~]# ls && echo yes || echo no
[root@xiaoshaozi ~]# [ -n "$aa" -a "$aa" -gt 23 ] && echo yes || echo no

# 其他

''	单引号
单引号中的特殊符号没有特殊意义:如 $、`

""	双引号
特殊符号没有含义,$、`\ 例外

``	反引号
``==>推荐使用==>$() bash中会先执行其中的命令

$()	引用系统命令

${}	变量引用
变量引用更加强大,可对变量进行字符串截取

$变量名 ==> ${变量名}

[]	条件判断

\$	转移符:输出 $


(命令;命令)		在子shell中执行
{ 命令;命令; }	在当前shell中执行

# 变量

定义、引用
$变量名 或 ${变量名}
[root@xiaoshaozi ~]# test=123
[root@xiaoshaozi ~]# test="$test"456
[root@xiaoshaozi ~]# test=${test}789

`` == $(命令)
[root@xiaoshaozi ~]# test=`date`
[root@xiaoshaozi ~]# test=$(date)
unset 变量名	变量删除
[root@xiaoshaozi ~]# set [选项]
选项:
	-u:	如果设定此选项,调用未声明变量时会报错(默认无任何提示)
	-x:	如果设定此选项,在命令执行之前,会吧命令先输出一次

set -u 更为常用

[root@xiaoshaozi ~]# set
#直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量

[root@xiaoshaozi ~]# env
# env看不到用户自定义变量,可以看到另一部分环境变量
local 定义局部变量

function Hello(){
        local text="Hello World!!!" #局部变量
        echo $text
}

shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止。
如果同名,Shell函数定义的local变量会屏蔽脚本定义的global变量。

# 环境变量

#使用export声明的变量即是环境变量
方式一:
export 变量名=变量值
[root@xiaoshaozi ~]# export ZS_AGE="18"

方式二:
[root@xiaoshaozi ~]# ZS_AGE="18"
[root@xiaoshaozi ~]# export ZS_AGE

[root@xiaoshaozi ~]# set
#直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量

[root@xiaoshaozi ~]# env
# env看不到用户自定义变量,可以看到另一部分环境变量

# 位置参数变量

$n	第n位参数,0-9以内用 $n,10以上的用 ${10}

$*	把所有参数看成一个整体

$@	代办所有参数,每个参数区分对待

$#	所有参数个数

# 预定义变量

$?	最后一次执行命令的返回状态

$$	当前进程的进程号(PID)

$!	后台运行的最后一个进程的进程号(PID)

其中$?最为常用

# 接收键盘

[root@localhost ~]# read [选项] [变量名]
选项:
	-p	“提示信息”:	  在等待read输入时,输出提示信息
	-t	秒数:		 read命令会一直等待用户输入,使用此选项可以指定等待时间
	-n	字符数:	read命令只接受指定的字符数,就会执行
	-s:			  隐藏输入的数据,适用于机密信息的输入
	
变量名:
	- 变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY
	- 如果只提供了一个变量名,则整个输入行赋予该变量
	- 如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,
	  而命令行上如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上

# 条件判断

文件类型

格式一:
[root@xiaoshaozi ~]# test [选项] 文件

格式二:最常用格式
[root@xiaoshaozi ~]# [ 选项 文件 ]

常用选项:
	-e	判断文件在不在
	-f	在且是普通文件
	-L	在且是链接文件
	-d	判断是否是目录
	-s  文件是否存在,或文件是否非空

文件权限

常用选项:
	-r-w-x	执行

两个整数比较

常用选项:
    num1 -eq num2		num1 == num2
    num1 -ne num2		num1 != num2
    num1 -gt num2		num1 >  num2
    num1 -lt num2		num1 <  num2
    num1 -ge num2		num1 >= num2
    num1 -le num2		num1 <= num2

字符串比较

常用选项:
    -z str		isBlank?
    -n str		isNotBlank?
    str1 == str2	str1==str2	
    str1 !=	str2	str1!=str2

多重条件

-a-o![root@xiaoshaozi ~]# [ -n "$aa" -a "$aa" -gt 23 ] && echo yes || echo no
yes

# $[ ] 、${ }、$( )、[ ]、[[ ]]、(())区别

$()命令替换,等价于````` ```出现嵌套时只能使用$()

$( ) 
finish_time=$(date)

$[ ]运算操作符,等价于$(( ))

xjh@ubuntu:~/iot/tmp$ echo $[3*5]
15
xjh@ubuntu:~/iot/tmp$ echo $[(3+4)*5]
35
xjh@ubuntu:~/iot/tmp$

${ } 变量使用操作符,有很多丰富的功能,简单引用$变量名

[ ] test测试简写形式

[[ ]] test升级版

(())

())是一个数学计算命令,用于对整数进行数学运算,比如((a=10+66))。

具体见博客数学计算命令(expr、(())、bc、declare等) (opens new window)中关于(())的介绍。

# 其他

# sh

sh
shell命令解释器
bash [options] [file]

选项:
    -c string:命令从-c后的字符串读取。
    -i:实现脚本交互。
    -n:进行shell脚本的语法检查。
    -x:实现shell脚本逐条语句的跟踪。

常用形式:sh -xc "ls"
[root@AY1307311912260196fcZ satools]# sh -x check_ssh_login.sh
+ DEFINE=30
+ cat /var/log/secure
+ awk '/Failed/ {++ip[$(NF-3)]} END {for (i in ip) print i"="ip[i]}'
++ cat /root/satools/black.txt
+ for i in '`cat /root/satools/black.txt`'
++ echo 121.42.0.16=1427
++ awk -F= '{print $1}'
+ IP=121.42.0.16
++ echo 121.42.0.16=1427
++ awk -F= '{print $2}'
+ NUM=1427
+ '[' 1427 -gt 30 ']'
+ grep 121.42.0.16 /etc/hosts.deny
+ '[' 1 -gt 0 ']'
+ echo sshd:121.42.0.16
+ echo vsftpd:121.42.0.16
+ for i in '`cat /root/satools/black.txt`'
++ echo 121.42.0.72=276
++ awk -F= '{print $1}'
+ IP=121.42.0.72
++ awk -F= '{print $2}'
++ echo 121.42.0.72=276
+ NUM=276
+ '[' 276 -gt 30 ']'
+ grep 121.42.0.72 /etc/hosts.deny
+ '[' 1 -gt 0 ']'
+ echo sshd:121.42.0.72
+ echo vsftpd:121.42.0.72

# 变量

# 数组

在Linux中,可以使用以下方式定义和使用数组:

  1. 使用括号和空格将数组元素括起来,每个元素之间用空格分隔。例如:

    array=("apple" "banana" "orange")
    
  2. 使用索引访问数组元素,索引从0开始。例如:

    echo ${array[0]} # 输出:apple
    
  3. 使用*@可以获取数组中的所有元素。例如:

    echo ${array[*]} # 输出:apple banana orange
    
  4. 使用#可以获取数组的长度。例如:

    echo ${#array[@]} # 输出:3
    
  5. 使用+=可以向数组中添加元素。例如:

    array+=("grape")
    echo ${array[*]} # 输出:apple banana orange grape
    
  6. 使用unset可以删除数组中的元素。例如:

    unset array[1]
    echo ${array[*]} # 输出:apple orange grape
    
  7. 使用循环遍历数组中的元素。例如:

    for item in ${array[*]}; do
        echo $item
    done
    

    循环来遍历数组,并获取每个元素的键和值

    #!/bin/bash
    
    # 定义一个数组
    my_array=(
        "key1=value1"
        "key2=value2"
        "key3=value3"
    )
    
    # 循环遍历数组
    for item in "${my_array[@]}"
    do
        # 使用IFS(内部字段分隔符)将键和值分割开
        IFS='=' read -ra array <<< "$item"
        key="${array[0]}"
        value="${array[1]}"
        
        # 输出键和值
        echo "Key: $key, Value: $value"
    done
    

    循环来遍历数组,并获取每个元素的索引和值。

    #!/bin/bash
    
    # 定义一个数组
    my_array=("value1" "value2" "value3")
    
    # 获取数组长度
    length=${#my_array[@]}
    
    # 循环遍历数组
    for ((i=0; i<$length; i++))
    do
        # 获取索引和值
        index=$i
        value=${my_array[$i]}
        
        # 输出索引和值
        echo "Index: $index, Value: $value"
    done
    
    在上述示例中,我们首先定义了一个数组my_array。然后,使用length变量获取数组的长度。接下来,使用for循环遍历数组,并在循环体中获取每个元素的索引和值。最后,我们输出索引和值。
    
    请注意,上述示例中的数组索引从0开始计数。如果你希望从1开始计数,可以在输出索引时将$i替换为$((i+1))

使用read命令结合循环结构来遍历以英文逗号分割的字符串

#!/bin/bash

# 定义一个包含逗号分割值的字符串
str="apple,banana,orange,grape"

# 使用IFS(内部字段分隔符)将字符串按逗号分割成数组
IFS=',' read -ra arr <<< "$str"

# 循环遍历数组中的每个值
for item in "${arr[@]}"; do
    echo "$item"
done

去除数组中的重复元素

#!/bin/bash

# 定义一个包含重复元素的数组
arr=("apple" "banana" "orange" "apple" "grape" "banana")

# 使用sort和uniq命令去除重复元素
sorted_arr=($(printf "%s\n" "${arr[@]}" | sort | uniq))

# 打印去重后的数组
echo "${sorted_arr[@]}"
#!/bin/bash

# 定义一个包含重复元素的数组
arr=("apple" "banana" "orange" "apple" "grape" "banana")

# 声明关联数组
declare -A unique_arr

# 遍历原始数组,将每个元素作为关联数组的键,如果键不存在,则将其值设置为1,否则将其值递增1
for item in "${arr[@]}"; do
    unique_arr["$item"]=1
done

# 打印去重后的数组(关联数组的键即为去重后的元素)
echo "${!unique_arr[@]}"

数组拷贝

在操作数组前拷贝一份数组是一种常见的做法,可以避免直接修改原始数组,从而避免循环过程中出现异常。你可以使用以下代码来复制一个数组:

# 原始数组
original_array=("element1" "element2" "element3")

# 复制数组
copied_array=("${original_array[@]}")

# 在复制的数组上进行操作
for element in "${copied_array[@]}"; do
  echo $element
done

关联数组允许你将键与值相关联,类似于Map数据结构。

要在Shell中使用关联数组,首先需要声明一个关联数组变量。以下是一个示例:

declare -A map_array

接下来,你可以通过键来添加或修改关联数组中的值。以下是一个示例:

map_array["key1"]="value1"
map_array["key2"]="value2"

要获取关联数组中的值,可以使用键来索引关联数组。以下是一个示例:

value1="${map_array["key1"]}"
value2="${map_array["key2"]}"

要删除关联数组中的键值对,可以使用unset命令。以下是一个示例:

bashunset "map_array["key1"]"

要遍历关联数组中的所有键值对,可以使用循环结构。以下是一个示例:

for key in "${!map_array[@]}"; do
  echo "Key: $key, Value: ${map_array[$key]}"
done

以上就是在Shell中使用关联数组作为Map数据结构的基本用法。通过使用关联数组,你可以在Shell中实现类似Map数据结构的操作。

shell判断变量未赋值和变量为空字符

  1. 判断变量是否未赋值:
if [ -z "${variable+x}" ]; then
    echo "变量未赋值"
else
    echo "变量已赋值"
fi

上述代码中,${variable+x}表示如果变量variable被赋值,则返回x,否则返回空字符串。通过使用-z选项,我们可以判断空字符串是否为真,从而判断变量是否未赋值。

  1. 判断变量是否为空字符串:
if [ -z "$variable" ]; then
    echo "变量为空字符串"
else
    echo "变量不为空字符串"
fi

上述代码中,-z选项用于判断变量是否为空字符串。如果变量为空字符串,则返回真,否则返回假。

# 数组合并

# 定义两个数组
array1=("元素1" "元素2" "元素3")
array2=("元素4" "元素5" "元素6")

# 合并数组
array3=("${array1[@]}" "${array2[@]}")

# 打印合并后的数组
echo "${array3[@]}"

//元素1 元素2 元素3 元素4 元素5 元素6



# 定义两个数组
array1=("元素1" "元素2" "元素3")
array2=("元素4" "元素5" "元素6")

# 合并数组并修改原始数组
array1=("${array1[@]}" "${array2[@]}")

# 打印合并后的数组
echo "${array1[@]}"

//元素1 元素2 元素3 元素4 元素5 元素6 

# 常用代码段

# 当前脚本文件目录

$(cd $(dirname $0);pwd)


#!/bin/bash

# 获取脚本所在路径
script_path=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
echo "脚本所在路径:$script_path"

# shell调用另一个脚本中的方法

  1. 直接调用:

如果你知道另一个脚本中的函数名称,你可以直接在当前的脚本中调用它。

例如,假设你有两个脚本:script1.shscript2.sh。在 script2.sh 中,有一个名为 myFunction 的函数。

# script2.sh
myFunction() {
    echo "Hello from myFunction in script2.sh"
}

你可以在 script1.sh 中直接调用这个函数:

# script1.sh
source script2.sh  # 导入script2.sh中的函数和变量等

myFunction  # 调用script2.sh中的函数
  1. 使用源命令 (source):

source 命令用于读取并执行指定脚本中的命令。这允许你在当前的shell环境中执行脚本,这意味着脚本中定义的任何函数或变量都将在当前环境中可用。

例如:

source /path/to/script2.sh  # 使用source命令导入script2.sh中的函数和变量等
  1. 使用点号 (.):

点号也可以用来执行脚本,与 source 命令类似,但它不会创建新的子shell。这意味着脚本中定义的任何函数或变量都将在当前环境中可用。

例如:

. /path/to/script2.sh  # 使用点号执行script2.sh中的函数和变量等
  1. 使用bash的 -c 选项:

如果你只想执行脚本中的一行命令,可以使用 -c 选项:

bash -c "source /path/to/script2.sh"  # 导入script2.sh中的函数和变量等,但不会在当前环境中使用它们,因为它们是在新的子shell中定义的。
  1. 注意点:
  • 当使用 source 或点号时,脚本中的任何变量或函数都将在当前shell环境中定义。如果你不想改变当前的环境,使用子shell可能是更好的选择。你可以使用 bash -c "command" 的形式来在子shell中执行命令。
更新时间: 2024年2月23日星期五凌晨12点17分