shell 是一种脚本语言。
脚本:本质是一个文件,文件里面存放的是 特定格式的指令,系统可以使用脚本解析器 翻译或解析 指令 并执行(它不需要编译)
shell 既是应用程序 又是一种脚本语言(应用程序 解析 脚本语言)

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#!/bin/bash

# 第一行主要用于指定解释器(包括:bin/sh、bin/bash、usr/bin/sh、usr/bin/bash)

# 我是单行注释

# 执行脚本的方式有三种:sh test.sh、. test.sh、source test.sh
# 决定路径执行:./test.sh,这种执行方式需要可执行权限,给脚本添加可执行权限:chmod 777 test.sh
# ./test.sh 方式执行需要指定解释器

# 三种变量:局部变量、环境变量、shell变量

# 变量:
name="hello" # 等号两边不能有空格
echo ${name} # 输出普通变量

function test() {
local name="test hello" # 局部变量用local关键字修饰,local必须在函数中使用
echo ${name}
}
name="only_read"
readonly name # readonly修饰符,修饰只读变量
echo $name # 或者 ${name}
name1="name1"
unset name1 # 删除变量,不能删除只读变量,删除后的变量无法被访问

# 单引号和双引号字符串的区别
name2='name2' # 单引号字符串,字符串内不能转义,不能包含变量
name3="name3\"${name2}" # 双引号字符串,字符串内可以有转义,也可以包含变量
echo ${name3}

# 字符串拼接
str1="1""2""3"'4''5' # 字符串字面量拼接
str2='6'
str3=${str1}${str2} # 字符串变量拼接
echo $str3
str4=`date`'==='$str3 # 命令拼接
echo $str4

# 获取字符串长度的5种方式
echo "hello" | wc -L # 1、wc -L 获取当前行的长度
expr length ${str3} # 2、使用expr length可以获取string的长度
echo "abc" |awk -F "" '{print NF}' # 3、awk获取域的个数
echo "Alex" |awk '{print length($0)}' # 4、通过awk+length的方式获取字符串长度
str5=Alex
echo ${#str5} # 通5、过 ${#str5} 的方式获取字符串长度

# 提取字符串
str6=123456789.jpg
echo ${str6##*56} # 从左向右截取最后一个string后的字符串 ,输出:789.jpg
echo ${str6#*56} # 左向右截取第一个string后的字符串,输出:789.jpg
echo ${str6%%56*} # 从右向左截取最后一个string后的字符串,输出:1234
echo ${str6%56*} # 从右向左截取第一个string后的字符串,输出:1234
echo ${str6:1:3} # 截取字符串,输出:234

# 数组
echo "====数组==== 数组越界不会报错"
array=(1 2 3 4 5 6)
echo ${array} # 输出数组,默认输出角标为0的数据
echo ${array[5]} # 输出数组角标为5的数据
array[0]=7 # 数组赋值
echo ${array} # 输出数组
array=([0]=0 [1]=1 [2]=2 [3]=3 [4]=4 [5]=5 [6]=6) # 数组初始化或赋值
echo ${array[6]} # 输出数组角标为6的数据
for((i=0;i<7;i++)) # for循环遍历数组
do
echo "array[$i]=${array[$i]}"
done
echo ${array[*]} # 输出整个数组
echo "a len: ${#array[*]}" # 输出数组的长度
a=(1 2 3 4 5 6)
b=("hello" "zhaixue.cc")
c=(${a[*]} ${b[*]}) # 数组拼接
echo ${c[*]} # 输出整个数组
echo "c length: ${#c[*]}" # 输出:c length: 8
unset c[6] # 删除数组中的元素
echo "c length: ${#c[*]}" # 输出:c length: 7

# 参数传递
# $0 代表执行的文件名
# $1 代表传入的第1个参数
# $n 代表传入的第n个参数
# $# 参数个数
# $* 以一个单字符串显示所有向脚本传递的参数。
# $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数
# $$ 脚本运行的当前进程号
# $! 后台运行的最后一个进程的ID
# $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

# 运算符 需注意:条件表法式需要放在方括号之间,并且要有空格。使用expr进行计算时需要使用反引号。
# 加法 expr $a + $b
# 减法 expr $a - $b
# 乘法 expr $a \* $b
# 除法 expr $b / $a
# 取余 expr $b % $a
# 赋值 a=$b
# 相等 [ $a == $b ]
# 不相等 [ $a != $b ]
a=10
b=20
c=`expr $a + $b` # 加法运算
echo "a + b : $c"

# 关系运算符
# 关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
# 关系用 [] 来表示,
echo "====关系运算符===="
a=1
b=2
# 检测两个数是否相等 [ $a -eq $b ] -eq
if [ $a == $b ]
then
echo "a和b相等"
else
echo "a和b不相等"
fi

# 或者

if [ $a -eq $b ]
then
echo "a和b相等"
else
echo "a和b不相等"
fi

# 检测两个数是否不相等 [ $a -ne $b ] -ne
if [ $a != $b ]
then
echo "a和b不相等"
else
echo "a和b相等"
fi

# 或者

if [ $a -ne $b ]
then
echo "a和b不相等"
else
echo "a和b相等"
fi

# 检测左边的数是否大于右边的 [ $a -gt $b ] -gt
if [ $a -gt $b ]
then
echo "a大于b"
else
echo "a等于b或a小于b"
fi

# 检测左边的数是否小于右边的 [ $a -lt $b ] -lt
if [ $a -lt $b ]
then
echo "a小于b"
else
echo "a等于b或a大于b"
fi

# 检测左边的数是否大于等于右边的 [ $a -ge $b ] -ge
if [ $a -ge $b ]
then
echo "a大于等于b"
else
echo "a小于b"
fi

# 检测左边的数是否小于等于右边的 [ $a -le $b ] -le
if [ $a -le $b ]
then
echo "a小于等于b"
else
echo "a大于b"
fi


# 布尔运算符
# 非运算 [ ! false ] !
# 或运算 [ $a -lt 20 -o $b -gt 100 ] -o
# 与运算 [ $a -lt 20 -a $b -gt 100 ] -a

# 逻辑运算符
# 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] &&
# 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] ||

# 布尔运算符和逻辑运算符的区别:
# 语法上,逻辑运算需要双括弧,布尔运算只需要单大括弧功能上,逻辑运算具有特殊的短路功能,
# 即是在AND运算中第一个表达式为false时则不执行第二个表达式,
# 在OR运算中第一个表达式为true时不执行第二个表达式。

# 字符串运算符
# 检测两个字符串是否相等 [ $a = $b ] =
# 检测两个字符串是否不相等 [ $a != $b ] !=
# 检测字符串长度是否为0 [ -z $a ] -z
# 检测字符串长度是否不为 0 [ -n “$a” ] -n
# 检测字符串是否为空 [ $a ] $

# 文件测试运算符
# 检测文件是否是块设备文件 [ -b $file ] -b file
# 检测文件是否是字符设备文件 [ -c $file ] -c file
# 检测文件是否是目录 [ -d $file ] -d file
# 检测文件是否是普通文件(既不是目录,也不是设备文件) [ -f $file ] 返回 true -f file
# 检测文件是否设置了 SGID 位 [ -g $file ] -g file
# 检测文件是否设置了粘着位(Sticky Bit) [ -k $file ] -k file
# 检测文件是否是有名管道 [ -p $file ] -p file
# 检测文件是否设置了 SUID 位 [ -u $file ] -u file
# 检测文件是否可读 [ -r $file ] -r file
# 检测文件是否可写 [ -w $file ] -w file
# 检测文件是否可执行 [ -x $file ] -x file
# 检测文件是否为空(文件大小是否大于0) [ -s $file ] -s file
# 检测文件(包括目录)是否存在 [ -e $file ] -e file
file="/home/westos/Desktop/textcpp/test.sh"
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi

# 运算指令
# (( )) 可以直接使用双圆括弧计算其中的内容,如((var=a+b))
# let 在计算表达式的时候我们可以直接使用let,如let var=a+b。
# expr
var=`expr a+b`
echo $var
# bc计算器 :bc计算器支持shell中的小数进行运算,并且可以交互式或者非交互式的使用
var=$(echo "(1.1+2.1)"|bc)
echo $var
# $[]: 计算中括弧中的内容,如echo $[1+2]


# 控制语句
a=1
b=1
# if-fi
if [ $a -eq $b ]
then
echo "a等于b"
fi

# if-else-fi
if [ $a -eq $b ]
then
echo "a等于b"
else
echo "a不等于b"
fi

# if else-if else
if [ $a -gt $b ]
then
echo "a大于b"
elif [ $a -lt $b ]
then
echo "a小于b"
else
echo "a等于b"
fi


# for循环
for a in 1 5 3 4
do
echo ${a} # 一次输出 1 5 3 4
done

array=(1 2 3 4 5 6 7 8)
for((i=0;i<8;i++)) # for循环遍历数组
do
echo "array[$i]=${array[$i]}"
done

for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done

# while循环

a=1
b=4
while [ $a -lt $b ]
do
echo "${a}"
let a++
done

# 无限循环
# for (( ; ; ))
# do
# echo 111
# done

# while true
# do
# echo 222
# done

# until循环
# until 循环执行一系列命令直至条件为 true 时停止
a=1
until [ $a -gt 3 ]
do
echo "until循环:$a"
let a++
done

# 跳出循环

var=1
while(( $var < 5 ))
do
if(( $var>3 ))
then
echo "break跳出循环"
break
fi
echo "$var"
var=`expr $var + 1`
done


var=1
while(( $var < 5 ))
do
echo "$var"
var=`expr $var + 1`
if [ $var -gt 2 ] && [ $var -lt 4 ]
then
echo "continue跳出循环:$var"
continue
elif [ $var -eq 4 ]
then
echo "break跳出循环"
break
fi
done


# case-esac多选择语句
echo "====case-esac多选择语句===="
var=100
case $var in # 分支语句开始
10)
echo "输出10"
;; # 相当于break
100)
echo "输出100"
;; # 相当于break
esac # 分支语句结束


# select-in语句
# select in是shell中独有的一种循环,非常适合终端的交互场景,它可以显示出带编号的菜单,
# 用户输入不同编号就可以选择不同的菜单,并执行不同的功能
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other";
do
echo "You have selected $var"
break;
done


# 函数

function funName() { # function可以省略
echo "执行了funName函数"
}
funName # 执行函数

funWithParam(){
echo "第一个参数为 $1 !"
echo "第十个参数为 ${10} !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73 # 执行函数并传递参数


# 重定向
#一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
# 1、标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
# 2、标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
# 3、标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

# > 标准输出覆盖重定向:将命令的输出重定向输出到其他文件中
# >> 标准输出追加重定向:将命令的输出重定向输出到其他文件中
# >& 标识输出重定向:将一个标识的输出重定向到另一个标识的输入
# < 标准输入重定向:命令将从指定文件中读取输入而不是从键盘输入
# | 管道符,从一个命令中读取输出并作为另一个命令的输入


# 输入重定向
# 可以让命令从文件中获取,这样本来的命令需要从标准输入stdin中获取,
# 转换为从我们的指定文件中获取。这样本来需要从键盘输入的命令就会转移到文件读取内容
# command1 < file
# 0<1.txt cat # 重定向输入流

# 输出重定向
# 输出重定向也是将本来需要输出标准输出文件stdout中转化为我们的指定文件中
# cat 1.txt > 2.txt # 将1.txt的内容覆盖到2.txt
# cat 1.txt 1> 2.txt # 将1.txt的内容覆盖到2.txt
# cat 1.txt >> 2.txt # 将1.txt的内容追加到2.txt
# cat 1.txt 1>> 2.txt # 将1.txt的内容追加到2.txt

# 标准错误文件重定向
# 借助标准错误文件的文件描述符来重定向stderr
# command 2>file
# 将stdout标准输出文件和stderr标准错误文件合并重定向到一个指定文件中
# command > file 2>&1

# /dev/null 文件
# 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null中,
# /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;
# 如果尝试从该文件读取内容,那么什么也读不到。
# 但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
# command > /dev/null

原文链接:https://www.jianshu.com/p/4599ef826895