shell scripts 学习记录
1. 环境变量的使用
shell中的环境变量分为env和set两种:
- set:仅在当前变量shell中成生效.
- env:仅在当前会话中生效
env # 查看环境变量
set
export # 设置环境变量
unset # 删除环境变量
$ # 引用变量
= # 设置变量赋值
# # 获取变量长度
2. Shell中的数组使用
Array
数组中可以存放多个值,Bash Shell 只支持一维数组,使用语法如下:
array_name=("cxk" 123 "wjk" 'wyf')
echo ${array_name[0]}
echo ${array_name[1]}
echo ${array_name[2]}
echo ${array_name[3]}
echo ${array_name[*]} # 一行输出所有元素
echo ${#array_name[*]} # 输出数组的长度 也可以使用${#array_name[@]}
# 执行结果输出为:
cxk
123
wjk
wyf
cxk 123 wjk wyf
4
注意:数组中可以存储
不同类型
的元素。
关联数组 (理解为python中的字典)
Bash 支持关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。使用语法如下:
# declare -A xxx
declare -A array_name
array_name["cxk"]=25
array_name["wyf"]=30
array_name[1949]="10.1"
# 取出关联数组汇总的元素,需要指定key
echo ${array_name["cxk"]}
echo ${array_name["wyf"]}
echo ${array_name[1949]}
echo ${#array_name[@]} # 数组的长度
echo ${#array_name["cxk"]} # 某个key对应value的长度 字符型key
echo ${#array_name[1949]} # 某个key对应value的长度 整数key
echo ${array_name[*]} # 一行输出所有的value
echo ${!array_name[*]} # 通过添加!取出所有的key
# 执行结果为:
25
30
10.1
3
2
4
10.1 25 30
cxk wyf 1949
- 字典需要根据指定key读取对应的value,且所有类型value的长度都可以通过#提取到。
- 输出value的是无序的。
3. shell中的基本运算符
常用的运算符有:
- 算数运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
原生的bash不支持简单的数学运算,可以通过expr
实现
#!/bin/bash
a=2
b=4
val=`expr ${a} + ${b}`
echo "a + b : $val"
val=`expr ${a} - ${b}`
echo "a - b : ${val}"
val=`expr ${a} \* ${b}` # 乘法需要注意使用 \*
echo "a * b : ${val}"
val=`expr ${a} / ${b}` # shell只支持整数运算,这种除不尽的,不同版本的shell结果不一致
echo "a / b: ${tmp}"
val=`expr ${b} / ${a}`
echo "b / a : ${val}"
# 执行结果为:
a + b : 6
a - b : -2
a * b : 8
a / b: 7
b / a : 2
- expr 后面表达式需要空格分明,即
$a + $b
,而不是$a+$b
- 完成的表达式需要用``包含起来,不是单引号,是ESC下面的符号。
4. shell 中流程控制语法
case…esac使用
echo "请输入1到4的数字:"
echo "你输入的数字为:"
read num
case ${num} in
1) command # 你输入的数字为1,执行后续的command
;; # 两个分号
2) command
;;
3) command
;;
esac # 结束符
if else条件中如果:
- 使用
-gt
、-lt
、eq
之类的逻辑操作,那么使用[...]括号
- 使用
<
、>
、==
之类的计算操作,那么使用((...))括号
# 字符串
read pattern
case "${pattern}" in
"wjk") echo "这里是王俊凯"
;;
"wyf") echo "skr,skr!"
;;
"cxk") echo "时长两年半"
;;
*) echo "找不到该明星"
;;
esac
注意:用
*)
来接受其它情况
5. 函数定义与调用
5.1 带返回值的函数
#!/bin/bash
# 函数声明
fun_with_return(){
echo "这个函数会对输入的两个数字进行相加..."
echo "输入第一个数字:"
read first_num
echo "输入第二个数字:"
read second_num
return $((${first_num} + ${second_num}))
}
fun_with_return # 调用函数
echo "相加的和为:$?!"
-------------------------------------------------------------
# 执行结果得到:
这个函数会对输入的两个数字进行相加...
输入第一个数字:
8
输入第二个数字:
9
相加的和为:17!
5.2 带传参数的函数
#!/bin/bash
# 带传参函数声明
fun_with_parameters(){
echo "第一个参数为$1"
echo "第二个参数为$2"
echo "第10个参数为$10"
echo "第10个参数为${10}"
echo "第11个参数为${11}"
echo "参数总共有$#个"
echo "直接作为一个字符串打印所有参数$*"
}
# 函数调用,后面直接跟上所传递的参数
fun_with_parameters wo xin 3 4 5 6 7 8 9 fei teng la
执行结果如下,可以发现第10个参数用{}
和不用{}
的结果是不一样的:
直接在vim
进入该文件,如下图所示,发现$
符号只能识别1~9
.
$10
不能获取第十个参数,获取第十个参数需要${10}
。
当
n>=10
时,需要使用${n}
来获取参数。
6. shell 中的输入/输出重定向
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
6.1 输出重定向
command1 > file1 # 输出重定向,将command1执行的结果输出到文件file1中(覆盖)
command2 >> file2 # 在末尾换行追加
# 实例,将两个结果先后写到一个文件里去
ls -l > forwardtest.sh
ls >> forwardtest.sh
# 可以看到>>的追加形式,会自动换到当前文件内容的下一行进行追加
6.2 输入重定向
# 统计forwardtest.sh文件的行数
wc -l < forwardtest.sh # 读取forwardtest.sh文件内容
# 执行结果为:
19
wc -l forwardtest.sh #
# 执行结果为:
19 forwardtest.sh
区别:前者不会输出文件名,后者会输出文件名.
6.3 错误文件重定向
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件1:
- 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
- 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
- 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
command 2>file1 # 将错误信息重定向到file1文件中
command 2>>file1 # 追加
# 希望将stdout和stderr合并输出到指定文件
command > file1 2>&1
或者
command >> file1 2>&1
command < file1 > file2 # 从file1读取,执行command后,将结果输出到file2
6.4 Here Document使用
# 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
command << delimiter
document
delimiter
# 实例:
cat << +
cxk
wjk
wyf
+
# 执行结果为:
cxk
wjk
wyf
解析:
cat
:表示command,需要执行的命令<<
:这是 Here Document 的开始标记,它告诉 Shell 后面会有一个多行文本块。delimiter
:可以由用户任意指定,当然一般用EOF比较合适,delimiter
用于表示后续文本的开始和结束。cxk wjk wyf
:表示多行文本块中的文本内容
。
执行过程:
- 当shell遇到
<< +
,则会从下一行开始读取,将所有行知道遇到+
为止的所有内容都是为一个文本块,然后将这个文本块作为输入传递给cat
命令,最后由cat
命令将该内容块打印到屏幕中
6.5 /dev/null的使用
# 如果希望执行某个命令的输出结果,不显示在屏幕上,那么可以将其重定向为/dev/null
command > /dev/null
# 如果希望屏蔽stdout和stderr
command > /dev/null 2>&1
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
小坑总结
单引号和双引号输出变量存在的问题
your_name="cxk"
# 使用双引号拼接
name="hello, "${your_name}" !"
name_1="hello, ${your_name} !"
echo $name $name_1
# 使用单引号拼接
name_2='hello, '${your_name}' !'
name_3='hello, ${your_name} !'
echo $name_2 $name_3
输出结果如下:
hello, cxk ! hello, cxk !
hello, cxk ! hello, ${your_name} !
显然,在双引号的情况下,${your_name}
是能够取出变量中的值.
第三种情况,虽然使用了单引号单引号,但是存在字符串拼接(这里不太理解,我个人感觉两层单引号,也算是双引号了),所以也能够取出${your_name}
中的值。而第四种情况name_3
中,单引号直接将里面的内容全部当作字符串了,因此直接输出。
condition 括号(())、[]、[[]]使用总结
总体建议:
- 对于
简单的条件判断
,使用方括号[]
是通用的做法,适用于大多数的 POSIX 兼容 Shell。- 对于
复杂的条件测试,如模式匹配、逻辑运算
等,使用双方括号 [[]] 更为合适,特别是在 Bash Shell 中。- 对于
数值比较和算术运算
,使用双圆括号(())
更为合适,它提供了便捷的数值操作。
多行注释
参考Here Document用法,将内容丢给:
,然而:
不会做任何操作,因此可以充当注释使用,
: >> delimiter
内容
delimiter
# 例如:
: >> COMMENT
内容
COMMENT
$()
和${}
的区别
$()
用于将一个命令的输出结果
作为一个整体来替换。例如,result=$(command) 会将 command 的输出结果存储在 result 变量中。${}
可以在变量的值
中插入命令的输出结果。- 通常情况下,推荐使用
$()
来进行命令替换,而使用${}
来进行变量替换和参数替换。