shell 语法
基础知识
如何学习
多动手多练习
不懂的查手册: help +具体命令
就知道了这个命令该怎么用了
shell 脚本能干啥
将重复的操作(主要是针对文件)组织起来,方便复用
常见的有很多种, 默认使用的是 bash
bash 脚本 需要 在开头写一个 #! /bin/bash
,指明bash为脚本解释器
如何运行一个 shell 脚本
首先完成 shell 脚本的内容,
再以可执行文件的方式运行(常用)
1. 直接执行的话,需要有可执行权限,查看文件的权限:ls -l test.sh
所以需要为我们的脚本增加可执行权限(r 读权限, w 写权限, x 可执行权限) chmod +x 脚本名.sh
.
2. 执行文件:可以在当前路径下执行,可以在绝对路径执行,可以在家目录下执行
也可以用解释器执行
注释
单行注释: #
后面的内容就是注释.
多行注释: (其中 EOF 可以换成自定义的字符串,保证头尾相同就可以)
:<<EOF
第一行注释
第二行注释
第三行注释
EOF
变量
shell 中的变量都是字符串,
定义一个变量
注意定义的时候,=
两边不能有空格
name1=acwing
name2='acwing'
name3="acwing"
使用定义好的变量
可以使用$
, 或者 ${}
. {}
是可选的,主要为了帮助解释器识别变量边界
只读变量
用 readonly 或者 declare 可以将变量设为只读变量
readonly name
declare -r name
删除一个变量的值
unset
可以删除变量的值
将某个变量的值删除后,这个变量的值就是一个空字符串
局部变量与全局变量
局部变量不能被子进程访问到,一般我们自己定义的变量都是局部变量
全局变量又称为环境变量,可以被子进程访问到
在命令行输入bash
可以进入一个子进程,同时这个父进程就休眠,在子进程输入 exit
就可以退出子进程,回到父进程
局部变量变为全局变量
export name # 第一种方法
declare -x name # 第二种方法
全局变量变为局部变量
export name=yxc # 定义环境变量
declare +x name # 改为自定义变量
字符串
定义字符串
定义一个字符串 可以用单引号,也可以用双引号,也可以不用引号(上面定义变量时的图)
单引号与双引号的区别:
单引号中的内容会原样输出,不会执行,不会取变量;
双引号中的内容可以执行,可以取变量;
取字符串长度
${#variable}
取子串
${variable:0:5} #从0开始取长度为 5 的子串
默认变量
shell 里面会有一些默认的变量
$0 就是 路径+文件名
执行脚本的时候可以向其传递参数
$1 传入的第一个参数
$2 传入的第二个参数
...
$100 传入的第100个参数
还要一些其他的默认变量
$# 传入的参数个数
$* 由所有参数构成的用空格隔开的字符串,如上例中值为"$1 $2 $3 $4"
$@ 每个参数分别用双引号括起来的字符串,如上例中值为"$1" "$2" "$3" "$4"
(一般没差别)
$$ 脚本当前运行的进程ID
$? 上一条命令的退出状态(exit code). 0表示正常退出,其他值表示错误
$(command) 返回command这条命令的stdout(可嵌套)
`command` 返回command这条命令的stdout(不可嵌套)
这里要注意 退出状态(exit code) 与标准输出(stdout) 的区别:
exit code 可以看做那个 main 函数的返回值(我们写main函数时,最后一句return 0. 这个 0 就是 exitcode)
stdout 可以看做是我们写的函数中的 cout 输入的内容
$(ls)
就返回了 ls 这条命令的stdout,也就是返回了当前目录下的所有文件
数组
定义数组时不需要指明大小
下标从 0 开始
只支持一维数组
数组中可以存放多个不同类型的值
定义数组
用小括号,数组元素之间的分隔符号是 空格<space>
array =(1 acwing "linux" "shell")
还可以直接定义某个位置的元素的值
array[0]=1
array[1000]=2
读取数组中的元素
${array[index]}
读取整个数组
${array[@]}
${array[*]}
数组的长度
这个数组的长度是实际的数组中的元素个数
${#array[@]}
${#array[*]}
expr 命令
expr
命令用于求表达式的值,格式为:
expr 表达式
expr 是一个命令,命令后的每一项都要用空格隔开. 所以表达式中的每一项也要用<space>
隔开
需要对特殊字符进行转义处理,也是就用 \
放在特殊符号前,(发现表达式运行错误时,可以试试转义)
对包含空格和其他特殊字符的字符串要用引号括起来
expr
的结果会输出在 stdout
中.(如果为逻辑关系表达式,则结果为真时,stdout输出1,否则输出0)
expr
的 exit code
:如果为逻辑关系表达式,则结果为真时,exit code为0,否则为1.
字符串表达式
length STRING
: 返回STRING的长度index STRING CHARSET
:返回CHARSET
中任意单个字符在STRING
中最前面的字符位置,下标从1开始.如果在STRING
中完全不存在CHARSET
中的字符,则返回0.substr STRING POSITION LENGTH
: 返回STRING
字符串中从POSITION
开始,长度最大为LENGTH
的子串.如果POSITION
或LENGTH
为负数,0或非数值,则返回空字符串.
算术表达式
只支持整数,算术表达式优先级低于字符串表达式,高于逻辑关系表达式.
+ -
加减运算.两端参数会转换为整数,如果转换失败则报错.
* / %
乘,除,取模运算.两端参数会转换为整数,如果转换失败则报错. *这个符号需要转义
() 可以改变优先级,但需要用反斜杠转义
shell 主要是处理文件,管道,字符串的,并不针对数字处理.
逻辑表达式
|
如果第一个参数非空且非0,则返回第一个参数的值,否则返回第二个参数的值,但要求第二个参数的值也是非空或非0,否则返回0. 如果第一个参数是非空或非0时,不会计算第二个参数.(短路原则)
&
如果两个参数都非空且非0,则返回第一个参数,否则返回0. 如果第一个参为0或为空,则不会计算第二个参数.
这里一个 | 就相当于 c++ 中的两个|, 起逻辑表达式的作用,并且逻辑表达式的返回值不仅仅是 0 和 1
< <= = == != >= >
比较两端的参数,如果为true,则返回1,否则返回0.
==
是 =
的同义词.
“expr” 首先尝试将两端参数转换为整数,并做算术比较,如果转换失败,则按字符集排序规则做字符比较.
()
可以改变优先级,但需要用反斜杠转义
read 命令
可以看做是 cin
read
命令用于从标准输入中读取单行数据.
当读到文件结束符时,exit code
为1,否则为0.
参数说明:
-p: 后面可以接提示信息
-t:后面跟秒数,定义输入字符的等待时间,超过等待时间后会自动忽略此命令
但是后面的命令会继续执行
echo 命令
echo 字符串
会把这个字符串给输出
格式化输出 printf
用的不多,所以偷个懒.放下 文档
test与[]
逻辑运算符
这里的逻辑运算符是 bash自己有的
之前的 |
和 &
是expr 自己定义的
&&
表示与,
||
表示或
同样具有具有短路原则:
expr1 && expr2
:当expr1为假时,直接忽略expr2
expr1 || expr2
:当expr1为真时,直接忽略expr2
利用短路原则可以实现 if else 的效果
这里的判断的是表达式的返回结果(exit code)
0 为真, 其他为假(与C/C++中的定义相反)
test 命令
主要用于: 判断文件类型以及对变量做比较
test 命令用的是 exitcode 来返回结果
判断文件类型与权限
整数间的比较
字符串比较
多重判断
[ ]
符号
[]
与test
用法几乎一模一样,更常用于if语句中.
另外[[]]
是[]
的加强版,支持的特性更多
注意事项:
[]
内的每一项都要用空格隔开[]
中括号内的变量,最好用双引号括起来- 中括号内的常数,最好用单或双引号括起来
if else switch
if then 形式
if confition
then
语句 1
语句 2
...
fi
if else 格式
if condition
then
语句1
语句2
...
else
语句1
语句2
...
fi
多层if-elif-elif-else
if condition
then
语句1
语句2
...
elif condition
then
语句1
语句2
...
elif condition
then
语句1
语句2
else
语句1
语句2
...
fi
switch
case…esac形式
类似于C/C++中的switch语句.
case $变量名称 in
值1)
语句1
语句2
...
;; # 类似于C/C++中的break
值2)
语句1
语句2
...
;;
*) # 类似于C/C++中的default
语句1
语句2
...
;;
esac