Linux常用命令--shell编程

摘要

  • 本文介绍shell编程的语法规则。

  • 本文基于CentOS8(x86_64)

文件头

1
2
#!/bin/sh :指定执行脚步的shell路径
#!/usr/bin/env bash :从env中查找指定的shell路径

如果没有为脚本设置文件头指定执行脚本的shell路径,则默认使用运行脚本的用户的shell

注释语法

  • # : 使用#进行单行注释,要讲某行注释,就在该行行首添加#

系统变量

变量名 描述
$SHELL 默认Shell
$HOME 当前用户家目录
$IFS 内部字段分隔符
$LANG 默认语言
$PATH 默认可执行程序路径
$PWD 当前目录
$UID 当前用户ID
$USER 当前用户
$HISTSIZE 历史命令大小,可通过HISTTIMEFORMAT变量设置命令执行时间
$RANDOM 随机生成一个0至32767的整数
$HOSTNAME 主机名

特殊变量

  • $0 : 当前脚本的文件名(间接运行时还包括绝对路径)。

  • $n : 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1 。

  • $# : 传递给脚本或函数的参数个数。

  • $* : 传递给脚本或函数的所有参数。

  • $@ : 传递给脚本或函数的所有参数。被双引号 "$@" 包含时,与 "$*" 不同,下面将会讲到。

  • $? : 上个命令的退出状态(0:success 非0:error),或函数的返回值。这个非常有用,常用语法是:if [ $? -eq 0 ]

  • $$ : 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。

  • $_ : 上一个命令的最后一个参数

  • $! : 后台运行的最后一个进程的 ID 号

小贴士
$*$@ 都表示传递给函数或脚本的所有参数,不被双引号 "" 包含时,都以独立个体"$1" "$2" … "$n" 的形式输出所有参数。
但是当它们被双引号 "" 包含时,"$*"会将所有的参数作为一个整体输出,"$@"依旧会以独立个体"$1" "$2" … "$n" 的形式输出所有参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# $*
for i in $*
do
echo $i
done
# sh test.sh 1 2 3 4,此时输出
1
2
3
4

# "$*"
for i in "$*"
do
echo $i
done
# sh test.sh 1 2 3 4,此时输出
1 2 3 4

# $@
for i in $@
do
echo $i
done
# sh test.sh 1 2 3 4,此时输出
1
2
3
4

# "$@"
for i in "$@"
do
echo $i
done
# sh test.sh 1 2 3 4,此时输出
1
2
3
4

获取参数数量:

1
paramSize=${#@}

自定义变量

字符串

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个字符串变量
s1="content"

# 定义一个变量接收之前定义过的变量,使用变量时需要使用 $+变量名称
s2=$s1

# 定义一个变量为之前定义过的变量与其它字符串的组合,此时可以使用 {} 将变量名称括起来以避免歧义
s3="${s1}_test"

# 定义一个变量接收日期字符串,``括起来的内容会优先执行,并把结果赋值给变量
s4=`date +%F' '%T`
now=`date +%Y%m%d_%H%M%S`

小贴士
$${ }都是用来引用变量的,${ }通常用于划定变量名的边界
当执行 echo "$aa"的时候系统会打印变量$aa的值,当执行echo "${a}a"时打印的是${a}和字母a,如果不需要为变量名划分边界的话,$a${a}是完全相等的。
除此之外,${ }还有一个重要的功能,就是文本处理:

  • 1.获取字符串长度
1
2
str='hello'
echo ${#str} # 5
  • 2.字符串切片${a:b:c}: 将字符串变量a从第b个位置开始向后截取c个字符,b是指下标,下标从0开始,c可以不指定,表示截取到字符串末尾
1
2
3
4
5
6
7
8
a='hello world!'
echo ${a:0:5} # hello
# 不指定c,表示从第6个字符开始截取到字符串末尾
echo ${a:6} # world!
# 截取从倒数第一个字符开始到字符串末尾的字符
echo ${a:(-1)} # !
# 截取从倒数第 6 个字符后的 5 个字符
echo ${a:(-6):5} # world
  • 3.替换字符串${a/b/c}: 将变量a中的b全部替换为c,开头一个正斜杠为只匹配第一个字符串,两个正斜杠为匹配所有字符。
1
2
3
4
5
6
7
a='hello hello world'
echo "${a/hello/hi}" # hi hello world
echo "${a//hello/hi}" # hi hi world
# 支持正则
str=123abc
echo ${str//[^0-9]/} # 123,将非数字替换为空
echo ${str//[0-9]/} # abc,将数字替换为空
  • 4.字符串截取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 格式:
# 删除匹配前缀
${parameter#word}
${parameter##word}
# 删除匹配后缀
${parameter%word}
${parameter%%word}
# 去掉左边,#最短匹配模式,##最长匹配模式。
% 去掉右边,%最短匹配模式,%%最长匹配模式。

# 示例
URL="http://www.baidu.com/baike/user.html"
# 匹配http://,以//为分隔符截取出右边字符串
echo ${URL#*//} # www.baidu.com/baike/user.html

# 以/为分隔符截取右边字符串,##表示尽可能多的删除,保留最少内容
echo ${URL##*/} # user.html
echo ${URL#*/} # /www.baidu.com/baike/user.html

# 以/为分隔符截取左边字符串,%%表示尽可能多的删除,即保留最少内容
echo ${URL%%/*} # http:
echo ${URL%/*} # http://www.baidu.com/baike
  • 5.变量状态赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 格式
${VAR:-string} # 如果 VAR 变量为空则返回 string
${VAR:+string} # 如果 VAR 变量不为空则返回 string
${VAR:=string} # 如果 VAR 变量为空则重新赋值 VAR 变量值为 string
${VAR:?string} # 如果 VAR 变量为空则将 string 输出到 stderr

# 示例
# 如果变量为空就返回 hello world!
VAR=
echo ${VAR:-'hello world!'} # hello world!
echo $VAR # 空

# 如果变量不为空就返回 hello world!
VAR="hello"
echo ${VAR:+'hello world!'} # hello world!
echo $VAR # hello

#如果变量为空就重新赋值:
VAR=
echo ${VAR:=hello} # hello
echo $VAR # hello

# 如果变量为空就将信息输出 stderr
VAR=
echo ${VAR:?value is null} # -bash: VAR: value is null

数字

  • 整数运算

    支持let$(( ))$[ ]expr四种方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    # 定义一个数字
    a1=10

    # 进行数字运算时,可以使用以下是几种运算方式中的一种
    # 加法和减法
    a2=$(($a1 + 5)) or a2=$((a1 + 5))
    a3=$[$a1 + 5] or a3=$[a1 + 5]
    # expr 要求运算符之间要有空格
    a4=`expr $a1 - 5` or a4=$(expr $a1 - 5)
    # let在引用变量时不需要$,另外运算符之间不能有空格
    let a5=a1-5

    # 乘法,注意使用expr方式时,* 要转义,
    a2=$((a1 * 5))
    a3=$[a1 * 5]
    # expr 要求运算符之间要有空格
    a4=`expr $a1 \* 5`
    # let在引用变量时不需要$,另外运算符之间不能有空格
    let a5=a1*5

    # 除法
    a2=$((a1 / 5))
    a3=$[a1 / 5]
    # expr 要求运算符之间要有空格
    a4=`expr $a1 / 5`
    # let在引用变量时不能有$,运算符之间不能有空格
    let a5=a1/5

    let$(( ))$[ ]中的变量都可以不加$前缀
    在进行整数运算时,$(( ))$[ ]的作用是等价的
    建议使用let$(( ))$[ ]的形式进行运算,其支持正常的运算逻辑,expr稍显笨拙,比如在进行带括号的运算时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    a2=$(((a1 - 5) * 5))
    a3=$[(a1 - 5) * 5]

    # expr 要求运算符之间要有空格
    # expr需要分开独立计算
    a4_1=`expr $a1 - 5`
    a4_2=`expr ${a4_1} \* 5`
    # let在引用变量时不需要$,另外运算符之间不能有空格
    let a5=(a1-5)*5

    小贴士
    $()和 `` 的作用一致,都是用来做命令替换用,一般用于将命令返回的结果传递给变量

  • 浮点数计算

1
2
3
4
5
6
7
8
9
10
11
12
13
x=10
y=3.211
a1=`echo "$x * 2 / $y" | bc` # a1为6

# scale=2 保留两位小数,相当于截断不会四舍五入
a2=`echo "scale=2; $x * 2 / $y" | bc` # a2为6.22
# awk的计算会四舍五入
a2=`echo "$x $y" | awk '{printf "%.2f\n",$1*2/$2}'` #a2为6.23

# bc运算时,整数位小于1不会显示
a3=`echo "scale=2; 2 / 3" | bc` # a3为.66,不会显示为0.66
# awk可以正确显示
a3=`echo 2 3 | awk '{printf "%.2f\n",$1/$2}'` #a3为0.67

bc是一个用于执行任意精度的数学计算的命令行计算器,支持基本的数学运算、逻辑运算、变量、函数等。
awk 的计算方式比 bc 更好一些,支持精度更为准确
bcawk的计算方式同样支持整数

2进制与10进制或16进制之间进行相互转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ibase=2 指定输入进制为二进制,默认为10进制。
# obase=16 指定输出进制为十六进制,默认为10进制。

# 10进制转2进制
echo "obase=2; 255" | bc # 11111111

# 10进制转16进制
echo "obase=16; 255" | bc # FF

# 2进制转10进制
echo "ibase=2; 11111111" | bc # 255
echo $((2#11111111)) # 255

# 16进制转10进制
echo "ibase=16; FF" | bc # 255
echo $((16#FF)) # 255

# 2进制转16进制,这里注意要先声明obase,再声明ibase,否则结果错误
echo "obase=16; ibase=2; 11111111" | bc # FF

# 16进制转2进制
echo "obase=2; ibase=16; FF" | bc # 11111111

数组

  • 普通数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#使用小括号定义数组,(元素之间用空格分隔,如果元素内容中就包含空格,可以使用双引号)
array=(Zero One Two Three)
array=("One" "Two Three" "Four")

# 或者使用索引进行赋值
array[0]="One"
array[1]="Two Three"
array[0]="Four"

# 遍历数组
# while遍历
i=0
while [ $i -lt ${#array[@]} ];do
echo ${array[$i]}
let i=i+1
done

# for遍历
for((i=0;i<${#array[@]};i++))
do
echo ${array[i]}
done
  • 关联数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 声明关联数组
declare -A array

# 初始化关联数组,注意这里key一定要加上[],因为其表示数组的下标
array=(["apple"]="red" ["banana"]="yellow" ["grape"]="purple")

# 或者添加键值对到关联数组中
array["apple"]="red"
array["banana"]="yellow"
array["grape"]="purple"

# 输出关联数组的内容
for key in "${!array[@]}"; do
echo "Fruit: $key, Color: ${array[$key]}"
done
  • 数组相关操作

1
2
3
4
5
6
7
8
# 打印数组内容
echo ${array[@]}

# 打印数组长度
echo ${#array[@]}

# 打印数组key
echo ${!array[@]}

从键盘设置变量:read

  • read命令格式

1
2
3
4
5
6
read [选项] [变量名]
选项:
-p “提示信息”:在等待read输入时,输出提示信息
-t “秒数”: read命令会一致等待用户输入,使用此选项可以指定等待时间
-n “字符数”: read命令只接受指定的字符数,就会执行
-s: 隐藏输入的数据,适用于机密信息的输入
  • 示例

1
2
3
4
5
6
7
8
read a # 此时会等待用户键入变量值,比如此时输入: 3
echo $a # 3

read -t 30 -p "Please input your username:" username
read -s -t 30 -p "Please input your passsword:" password

read a b c # 可以设置多个变量,比如此时输入: 1 2 3
echo "${a}_${b}_${c}" # 1_2_3

test命令

  • test作用检查某个条件是否成立,返回值为0(真)或者其他值(假),可通过echo $?查看返回值,也常用于循环和条件语句。

  • test一般有三种用法:

    测试文件或者文件夹是否存在
    字符串比较
    数值比较

测试文件或者文件夹是否存在

  • 参数说明

1
2
3
4
5
6
7
8
-d dir :是否为目录,是目录为真
-f file :是否为常规文件,是文件为真
-x file/dir :是否可执行,可执行为真
-r file/dir :是否可读,可读为真
-w file/dir :是否可写,可写为真
-a file/dir :文件或目录是否存在,存在为真
-e file/dir :文件或目录是否存在,存在为真
-s file :文件大小是否非0,非0为真
  • 示例

1
2
3
4
5
$ test -f test.sh
$ echo $?

$ test -d dir
$ echo $?

字符串比较

  • 参数说明

1
2
3
4
5
6
参数	    说明
-z 当str为空时返回真
-n 当str为非空时返回真
= 两个字符串相等时返回真
== 两个字符串相等时返回真,同=
!= 两个字符串不相等时返回真
  • 示例

1
2
3
4
5
6
7
8
9
10
11
$ test -z ''
$ echo $? # 输出0

$ test -n 'hello'
$ echo $? # 输出0

$ test 'hello' == 'world'
$ echo $? # 输出1

$ test 'hello' != 'world'
$ echo $? # 输出0

数值比较

  • 参数说明,使用><等运算符时需要转义 \>\<,或者使用如下参数替换对应的运算符

1
2
3
4
5
6
7
参数	    说明
-eq 等于时返回真 ==
-ne 不等于时返回真 !=
-lt 小于时返回真 <
-le 小于等于时返回真 <=
-gt 大于时返回真 >
-ge 大于等于时返回真 >=
  • 示例

1
2
$ test 1 -lt 2
$ echo $? # 输出0

逻辑运算

  • 参数说明

1
2
3
4
参数	  说明
-a 逻辑与,二者都为真则为真
-o 逻辑或,二者任意一个为真则为真
! 逻辑非
  • 示例

1
2
3
4
5
$ test ! -e test.sh
$ echo $?

$ test -f test.sh -a -d dir
$ echo $?

shell中的用法

1
2
3
4
5
6
i=$1
if test $i -lt 5;then
echo "$i < 5"
else
echo "$i >= 5"
fi

条件判断

  • 在使用ifwhile等语句时,需要进行条件判断,上面我们已经见到一个while的例子,其使用[ ]来定义条件判断

  • 实际上除了[ ]以外,我们还可以使用test(( ))[[ ]]来进行条件判断,那么他们之间有什么区别吗?

    • [ ]test命令的另一种形式,例如 test a == b 等同于 [ a == b ],注意 [ 后和 ] 前都需要有空格,并且==两边也都要有空格
    1
    2
    3
    test 'hello' != 'world' ==>  [ 'hello' != 'world' ]
    test ! -e test.sh ==> [ ! -e test.sh ]
    test $i -lt 5 ==> [ $i -lt 5 ]
    • [[ ]][ ]的增强版,其在如下几个方面进行了增强:
      • 1.在[[ ]]中使用>< 进行数值比较时不需要转义,但是不支持>=<=
      • 2.支持&&||
      1
      2
      3
      4
      # 以下三种作用相同
      [[ $a > 3 && $a != 10 ]]
      [ $a > 3 -a $a != 10 ]
      [ $a > 3 ] && [ $a != 10 ]
      • 3.[[ ]]在比较字符串时支持正则匹配和通配符匹配
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 通配符匹配
      a="linux"
      [[ $a == l?nu? ]] # 0
      [[ $a != li* ]] # 1

      # 正则匹配 `=~`
      a="linux"
      [[ $a =~ ^li ]]
      [[ $a =~ ^li[abn]ux ]]
    • (( )) 用于条件判断时只能进行数值比较,运算符不需要转义,而且不支持-lt-gt等等
      1
      (( $i <= 5 ))

    小贴士
    (( ))除了用于条件判断外,还有三种用法:
    1.与$结合使用进行数学运算 : $(( )),如:a=$((5 + 3)),a=$((2#1010)) 二进制转10进制,等等
    2.在for循环命令中控制循环 : for((i=1;i<10;i++))
    3.改变变量的值,且变量前不需要$ : ((i++)),没有任何输出,只是改变i的值

  • 推荐在进行条件判断时使用[[ ]],运算符不需要转义,而且支持正则

流程控制语句

if语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 语法1
# 因为多个语句写在同一行,所以要用;隔开
if [[ -d $path ]];then
echo "dir"
fi
# 其实也可以这样写
if [[ -d $path ]]
then
echo "dir"
fi

# 语法2
if [[ -d $path ]];then
echo "dir"
else
echo "not dir"
fi

# 语法3
if [[ -d $path ]];then
echo "dir"
elif [[ -f $path ]];then
echo "file"
else
echo "not match"
fi
  • 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 判断参数个数是否正确,只允许输入一个参数
paramSize=${#@}
if [[ $paramSize == 0 || $paramSize > 1 ]];then
echo "ERROR:需要一个参数!!!"
exit 1
fi

# 判断参数是否为大于0的数字
if [ "$1" -gt 0 ] 2>/dev/null ;then
echo "$1 id number"
else
echo "ERROR: $1 is not number!"
exit 1
fi

# 判断参数是否为整数数字,包含正整数,0,负整数
expr $1 "+" 0 &> /dev/null
if [[ $? == 0 || $1 == 0 ]];then
echo "$1 is number"
else
echo "$1 not number"
fi

# 如果导出文件已经存在则先删除
if [[ -a $bookId.tar.gz ]];then
rm -rf $bookId.tar.gz
fi

# 判断字符串是否不为空
if [[ -n $str ]];then
echo "$str is not null"
else
echo "$str is null"
fi

case语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case "$1" in
start)
echo "start"
;;
reload)
echo "reload"
;;
stop)
echo "stop"
;;
status)
echo "status"
;;
*)
echo "$0: Usage: {start|status|stop|reload}"
# 非0,非正常退出,exit 0 :正常退出
exit 1
;;
esac

select语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# select.sh
select var in "Linux" "UNIX" "Windows" "Other"
do
echo "You have selected $var"
# 跳出循环
break
done

# 运行
$ sh select.sh
1) Linux
2) UNIX
3) Windows
4) Other
#? 2 # 输入2,表示选择第二个
You have selected UNIX

while循环语句

1
2
3
4
5
6
a=0
while [[ $a < 5 ]]
do
echo $a
((a++))
done

until循环语句

  • 类似while,不同点是条件判断为假时才执行

1
2
3
4
5
6
7
a=5
# 不满足条件时执行
until [[ $a == 0 ]]
do
echo $a
((a--))
done

for循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 语法1
for((i=0;i<=10;i++))
do
echo $i
done

# 语法2
# 使用{}和seq生成序列
for i in {1..9} # 1 2 3 4 5 6 7 8 9
do
echo "this is $i"
done

for i in {1..9..2} # 1 3 5 7 9
do
echo "this is $i"
done


for i in `seq 1 9` # 1 2 3 4 5 6 7 8 9
do
echo "this is $i"
done

for i in `seq 1 2 9` # 1 3 5 7 9
do
echo "this is $i"
done

# 直接列出要遍历的项
for day in Sun Mon Tue Wed Thu Fri Sat
do
echo "The day is : $day"
done

# 将命令执行结果作为要遍历的内容
for line in `cat file`
do
echo $line
done

# 遍历所有参数
for p in $@
do
echo $p
done

其它语句

  • break : 退出本层循环,继续执行本层循环体后面的代码,注意是退出本层循环体,如果是嵌套循环,则退出break所在层的循环,并非所有的循环

  • continue : 跳过本次循环,不再执行continue下面的代码,回到循环判断式判断是否继续执行循环

1
2
3
4
5
6
7
8
9
10
11
12
for((i=0;i<=10;i++))
do
if [[ $i == 0 ]];then
# 不执行后面的语句直接进行下次循环
continue
fi
echo $i
if [[ $i == 8 ]];then
# 跳出循环体,不再进行循环
break
fi
done
  • exit 0 : 正常退出

  • exit 1 : 非0,非正常退出

  • shift : 每执行一次,参数序列顺序左移一个位置,$#的值减1,移出去的参数不再可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# shift.sh
if (( $# <= 0 ));then
echo "Not enough parameters"
exit 0
fi
sum=0
while [[ $# >= 0 ]]
do
sum=$((sum + $1))
shift #因为每次执行shift,所以上面的$1永远都是下一个参数
done
echo $sum

# 执行
$ sh shift.sh 1 2 3 4 5 # 输出 15

shell函数的定义与使用

  • shell函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 函数定义标准写法
function name() {
statements
[return value]
}

function : Shell 中的关键字,专门用来定义函数
name : 函数名
statements : 函数要执行的代码,也就是一组语句;
return value : 函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值,这一部分可以写也可以不写。
由{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。

# 函数定义时也可以不写 function 关键字
name() {
statements
[return value]
}
# 如果写了 function 关键字,也可以省略函数名后面的小括号
function name {
statements
[return value]
}
  • shell函数的调用

1
2
3
4
5
# 如果不传递参数,直接给出函数名字即可
name

# 如果传递参数,多个参数之间以空格分隔
name param1 param2 param3

和其它编程语言不同的是,Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,在方法体内引用时通过$1,$2,……来使用传递过来的参数

  • 示例

1
2
3
4
5
6
# 函数定义,不需要接收参数
function start(){
echo "start"
}
# 函数调用,在shell脚本中,可以将调用放在定义的前面
start
1
2
3
4
5
6
7
8
# 函数定义,需要接收参数,假设这里需要接收两个数字并求和
function sum(){
return $(( $1 + $2 ))
}
# 函数调用
sum 1 2
# 输出结果
echo $?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 多个参数求和
function getsum(){
# 定义局部变量,变量前面要加上local关键字
local sum=0
for n in $@
do
((sum+=n))
done
return $sum
}
# 函数调用
getsum 10 20 55 15 #调用函数并传递参数
# 输出结果
echo $?
  • shell函数中定义变量,也是全局变量,在函数体外也可以被调用,要是希望在函数体内定义的变量仅能被函数体内调用,则需要在变量前加上local关键字

运行shell

  • sh test.sh : 运行shell脚本

  • sh -x test.sh : 执行脚本,并显示全部过程

  • sh -n test.sh : 不执行脚本,只检查语法错误

  • 也可以为shell脚本授予执行权限,然后通过.关键字执行,比如. ~/test.sh,或者直接通过脚本路径运行~/test.sh(注意要设置文件头指定shell)

小贴士

  • 如果在windows环境下编写的脚步,上传到linux后需要先执行dos2unix进行编码转换,否则不能正确执行
1
dos2unix test.sh
  • 如果dos2unix命令不存在,可以通过yum进行安装
1
yum install dos2unix -y