Shell学习笔记

这段时间工作中需要写些Spark大数据脚本,用到了Shell脚本,基本都是靠抄前人的代码,就是替换一下SQL,写完了有时候也有点懵🤔️。好久之前就想好好学一下sh脚本,一直没有时间💦,这次要认真学完💪

第一个shell脚本

1
2
3
4
5
6
7
8
#!/bin/bash

:<< EOF
注释1
注释2
EOF

echo "Hello world!"

添加执行权限:chmod +x start.sh 后执行脚本:./start.sh

shell变量

变量定义和操作

  • 字母A-Z ➕ 数字🔢 ➕ 下划线_
  • 不能以数字开头、不能包含bash关键字⚠️
1
2
username="Bob"
echo "username is \"${username}\""
  • 变量赋值等号两边不能有空格

单引号字符串会原样输出,双引号可以转义字符、读取变量

  • 使用readonly指定只读变量,只读变量不能被修改

  • 使用unset删除变量

1
2
3
4
5
6
7
#!/bin/bash

readonly TEN=10

username="Bob"
echo "username的值为:\"${username}\""
echo "TEN is ${TEN}"

字符串操作

  • 字符串长度
1
2
username="John"
echo "length is ${#username}"
  • 取子字符串
1
echo "substring 1-4: ${username:1:2}"  # ohn:从第1位开始,取2个字符
  • 查找子串
1
# todo

shell数组

数组定义

shell不支持多维数组,各数组元素用空格🈳️隔开,下标范围没有限制。

1
2
names=("user1" "user2" "user3")
names[4]="user4"

读取数组元素

1
2
3
4
5
# 获取某个下表元素
user1=${users[0]}

# 获取所有元素
echo "${users[@]}"

数组长度

1
2
3
4
5
6
7
8
9
10
11
# 获取数组的长度
echo "array length is ${#users[@]}"
echo "array length is ${#users[*]}"

# 获取某个数组元素的长度
echo "elem length is ${#users[0]}"

# 循环数组
for element in ${users[*]}; do
echo "user is: ${element}"
done

注释

1、单行注释

1
2
3
# !/user/bin
# author:
# since:

2、多行注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:<<EOF
第一行注释
第二行注释
...
EOF

# 或者
:<<!
第一行注释
第二行注释
...
!

# 或者(教程里的写法,实测不行🙅‍♂️)
:<<'
第一行注释
第二行注释
...
'

shell传递参数

$1为第一个参数、$2为第二个参数….

特殊含义的参数:

参数名称 参数含义
$# 传递的参数的个数
$* 显示所有的参数(字符串形式
$$ 显示当前脚本运行的进程号
$? 最后脚本退出的状态,0表示没有错误,其他表示有错误

运算符

1
2
3
# 表达式用`且必须有空格
val=`expr 2 + 2`
val=`$1 == $2`

echo

大学的时候,有个老乡学姐的QQ昵称就是Echo这几个字母,然后去查了下是“回音”的意思。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 字符串可以不用引号
echo "Hello world!"
echo Hello world!

# 转义字符
echo "\"Hello world!\""

# 显示变量
echo "value of param is ${param}"

# 定向结果至文件
echo "file content" > file

# 显示命令执行结果
echo `date`

printf格式化输出

1
2
3
4
5
printf "%d只乌龟🐢%d条%s\n" 1 4 '腿🦵'

# 格式化输出 -:向左对齐 10:对齐位数
printf "%-10s%-10s%-10s\n" "标题1" "标题2" "标题3"
printf "%-10s%-10s%-10s\n" "值1" "值2" "值3"

test命令

数值比较

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

参数 说明
-eq 是否相等
-ne 是否不相等
-gt 是否大于
-lt 是否小于
-le 是否小于等于
-ge 是否大于等于

字符串比较

参数 说明
= 是否相等
!= 是否不相等
-z 字符串 字符串长度是否为0
-n 字符串 字符串长度是否不为0

文件比较

参数 说明
-e 文件名 如果文件存在则为真
-r 文件名 如果文件存在且可读则为真
-w 文件名 如果文件存在且可写则为真
-x 文件名 如果文件存在且可执行则为真
-s 文件名 如果文件存在且至少有一个字符则为真
-d 文件名 如果文件存在且为目录则为真
-f 文件名 如果文件存在且为普通文件则为真
-c 文件名 如果文件存在且为字符型特殊文件则为真
-b 文件名 如果文件存在且为块特殊文件则为真

与 或 非

参数 说明
-a
-o
!

猜数小游戏

  • $[$RANDOM % 100] 产生0-100之间的随机数
  • read -p “placeholer” param 将用户输入存入 param 变量中
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
:<<!
前段时间组里团建,吃饭的时候和领导一桌,
吃饭后气氛有点尴尬😅,领导搞了个游戏,
每次由某个人准备一个数字并保密,然后大家依次轮流猜,
准备数字的人告知此次猜数是大了还是小了,只能在每次缩小的范围中选数,
最后猜中的人要接受惩罚。
学完shell脚本变量、注释、数组、参数传递、随机数、输出、if判断后写的猜数小游戏!
!

#!/bin/bash

# 接收用户输入的数值
input_num=0
# 每次游戏产生的目标数字
random_num=0
# 是否继续游戏标志位
flag=""

echo "欢迎来猜猜看!"
while true
do
read -p "开始游戏?> " flag
if test ${flag} = "Y"
then
random_num=$[$RANDOM % 100]
while test ${input_num} -ne ${random_num}
do
read -p "请输入数字 > " input_num
if test ${input_num} -gt ${random_num}
then
echo "大了"
else
echo "小了"
fi
done
echo "恭喜!猜对了🎉"
else
echo "退出游戏.."
kill $$
fi
done

流程控制

if-elif-else

语法:

1
2
3
4
5
if condition
then
command1
command2
fi
1
2
3
4
5
6
7
8
9
10
if condition
then
command1
command2
elif condition2
then
command3
else
command4
fi

例子🌰:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

age=35
std=30

if [ ${age} -gt ${std} ]; then
echo "年龄合格🈴️"
elif [ ${age} -eq ${std} ]; then
echo "巧了㊗️"
else
echo "无缘👋"
fi

for

语法:

1
2
3
4
5
for item in val1 val2 val3
do
command1
command2
done

例子🌰

1
2
3
4
5
6
7
8
#!/bin/bash

sum=0
for item in 1 2 3 6
do
((sum=sum+item))
done
echo "sum is ${sum}"

while

语法:

1
2
3
4
while condition
do
command
done

case-esca

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case ${val} in
case1)
command1
command2
;;
case2)
command3
commmad4
;;
*)
command5
command6
;;
esac

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

read -p "input: " val

case ${val} in
"a")
echo "input a"
;;
"b")
echo "input b"
;;
*)
echo "input other"
;;
esac

函数

linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。

语法:

1
2
3
function fun_name() {
command
}

函数的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

function test1() {
echo "running test1."
echo "param1 is $1"
echo "param2 is $2"
}

echo "start call test1()"
test1 p1 p2 p3
echo "end call test1()"

#####
start call test1()
running test1.
param1 is p1
param2 is p2
end call test1()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

function add_two_num() {
echo "请输入第一个数字"
read num1
echo "请输入第二个数字"
read num2
echo "输入的两个数字分别为:${num1} ${num2}"
return $((${num1} + ${num2}))
}

echo "对输入的两个数字求和"
add_two_num
echo "和为:$?"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

function find_max() {
local val1=$1
local val2=$2
local val3=$3
local temp=0
if [ ${val1} -ge ${val2} ]
then
temp=${val1}
else
temp=${val2}
fi
if [ ${temp} -ge ${val3} ]
then
return ${temp}
else
return ${val3}
fi
}

find_max 3 1 6
echo "max value is $?"

文件包含

两种方式:. filename 或者 source filename

1
2
3
4
5
# url.sh
#!/bin/bash

URL_BAIDU="https://www.baidu.com"

1
2
3
4
5
6
7
8
#!/bin/bash

# 两种方式导入 url.sh 文件
#. ./url.sh
source ./url.sh

echo "导入的url为:${URL_BAIDU}"

shell输入输出重定向

输入输出重定向

命令 说明
command > file 将输出重定向到file
command < file 将输入重定向到file
commad >> file 将输出以追加的方式重定向到file
n >& m 将输出文件m和n合并
n <& m 将输入文件m和n合并
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入
1
2
3
echo "Hello world!" > tmp.txt

wc -l < tmp.txt

如果希望 stderr 重定向到 file,可以这样写:

1
$ command 2 > file

如果希望 stderr 追加到 file 文件末尾,可以这样写:

1
$ command 2 >> file

2 表示标准错误文件(stderr)。

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

1
2
3
4
5
$ command > file 2>&1

或者

$ command >> file 2>&1

如果希望对 stdin 和 stdout 都重定向,可以这样写:

1
$ command < file1 > file2

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

命令 说明
stdin(文件描述0 标准输入
stdout(文件描述符1 标准输出
stderr(文件描述符2 标准错误

Here document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

它的基本的形式如下:

1
2
3
command << delimiter
document
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:

  • 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
  • 开始的delimiter前后的空格会被忽略掉。
1
2
3
4
5
6
7
8
wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF

# output
3

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

1
$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出”的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

1
$ command > /dev/null 2>&1

这里的 2 > *之间不可以有空格,*2> 是一体的时候才表示错误输出。