bash基础引导
这篇文章是从之前的博客里面分离出来的,并做了一些整理。
最初学习shell脚本是在2015年,工作的这几年主要还是用php处理相关的需求,shell更多是运维小伙伴来处理些东西。这就导致我的shell基本忘没了。这不,这周想写写shell,碰到好多的语法错误,记录一下。
变量,字符串
var="aaabbbccc"
# 字符串替换
echo ${var/aaa/ddd}
# 字符串截取
echo ${var:0:8}
echo ${var:0:-1} # zsh支持,测试用bash3.2.57不支持
# 获取字符串长度
echo ${#var}
# 字符串拼接
echo 'my name '$var'!'
echo "my name $test!"
echo "my name ${test}!"
注意点:
- 等号两边不能有空格,否则会被识别为命令
- 单引号不解析变量,双引号解析
内置变量
$0
: 脚本名称$n
: 第n个参数$#
: 参数个数$?
: 上个语句的返回值$@
: 脚本参数字符串,与$*
略有区别$*
: 脚本参数字符串$$
: pid
注意点:
- $@,$*都可以代表参数字符串,略有区别,$*加上引号时候"$*",那么它表示"$1 $2…$n"的一个字符串,而不是由每个参数组成的列表,$@行为不变。所以下面的代码会输出一行空格分开的参数列表:
for i in "$*"
do
echo $i
done
命令
在shell脚本中执行命令、重定向之类的和在终端上执行没啥区别,直接写命令就行。例如:
echo "aaa" > a.log
echo "who am i `whoami`"
echo "who am i $(whoami)"
字符串拼接命令并运行:
c="ls"
c="$c /tmp"
$c
# or
echo "$c /tmp" | sh
流程控制
流程控制这块花费的时间比较多,主要是bash的判断写法很多,初学者非常容易混淆,下面做一些简单的解释:
总览
首先bash中的判断只有如下几种:
- 单中括号
if [ expression ]; then
- test
if test expression; then
- 双中括号
if [[ expression ]]; then
- 双小括号
if (( expression )); then
单中括号 & test
这两种方式是最古老的,也是兼容性最好的。
这两个放在一起说明是一样的,也即test
和[
是完全一样的两个东西,man页面都是一样的。所以两者完全可以互换,在括号比较多的时候通常会用test来替换,这样看起来更简洁一些。
这两种判断主要用在两个地方:
- 数字判断,例如:
if [ 10 -gt 5 ]; then
- 字符串判断,例如:
if [ "aa" = "bb" ]; then
到这里可能又混淆了,为什么字符串和数字还不一样呢?其实本质上判断条件是test
或[
命令的参数,而参数规定就是这样写。
这里面如果用大于号或者小于号之类的会出发重定向,所以比较字符串大小不推荐用这种方法。
除了这两种用法,还有就是文件相关的判断,有很多的选项,具体可以查看文档。例如:
比较文件新旧
# 如果file1的修改时间比file2新
if [ file1 -nt file2 ]; then
# 反之,旧
if [ file1 -ot file2 ]; then
判断是否为空文件
# 文件非空返回true
if [ -s /tmp/b.sh ]; then
判断多个文件的简便写法
# -a 等同于 &&,同理,-o为 ||
if [ -f $file -a -f $file2 ]; then
不过文档上更建议这种写法,理由是-a或-o不好理解:
if test -f $file && test -f $file2; then
判断字符串是否为空:
# -z 字符串为空返回true
if [ -z "$*" ]; then
# -n 字符串不为空返回true
if [ -n "$*" ]; then
判断文件是否为符号连接
if [ -h $file ]; then
# 注意这里不能用-f,-f会找到符号链接所指的文件,可能一直为true
双中括号
双中括号与单中括号功能类似,上面所有的代码都可以换成双中括号的写法。双中括号可以看作是单中括号的加强版,毕竟是新的产物,通常会解决一部分痛点。相比之下双中括号有以下几个特点:
- 扩展glob,也就是支持一些通配符之类的,例如:
if [[ "strings" == string* ]]; then
注意,后面的glob不能加双引号。
- 扩展文件名,例如:
if [ -e *.sh ]; then
这种写法会扩展glob,如果当前目录下有一个.sh文件,返回true。如果有多个,会有语法错误。
if [[ -e *.sh ]]; then
这种写法不会扩展glob,也就是只有当前目录下有*.sh
文件,才会返回true。
- 逻辑运算 两者逻辑运算写法不同,分别是:
if [ $a -gt 10 -a $a -lt 20 ]; then
# 或者
if [ $a -gt 10 ] && [ $a -lt 20 ]; then
# 后者的写法
if [[ $a > 10 && $a < 20 ]]; then
- 正则表达式
双中括号的写法,如果用了
=~
,那么右侧参数会被识别为正则表达式,例如:
# 同样不能使用双引号
if [[ "$1" =~ ^[0-9]+$ ]]; then
- 变量不需要添加引号 双中括号对变量做了特殊处理,防止分裂,所以变量内有空格不影响。但是保持变量两侧有双引号是一个好的习惯。
双小括号
这种方式也是用来数学运算,相对单中括号来说更像其他编程语言,例如:
if (( 12 > 10 )); then
if (( 12 > 10 && 5 < 6 )); then
# 还能直接进行算数计算,单中括号是不支持的
if (( 12%6 == 0 )); then
简单总结
简单粗暴一点就是:数字相关的比较,用双小括号;字符串相关的用双中括号。当然如果精益求精酌情使用即可。
分支语句、循环
a="wu"
case "$a" in
"as") echo 'as';;
"zz"|"xx") echo "zz and xx";;
*) echo "default";;
esac
# 循环
for var in {1..7}; do
tmp=$(( $var%2 ))
# echo $tmp
if [ $tmp -eq 0 ]; then
echo $var
fi
done
# 传统for循环
for ((i=0; i < 5; i++)); do
echo $i
done
# 输出文件信息
for file in $(ls); do
if [ -e $file ] && [ ! -d $file ] && [ -r $file ]; then
wc $file
fi
done
# while循环
tmp=0
while [ true ]; do
if [ $tmp -gt 5 ]; then
break
fi
echo $tmp
((tmp=$tmp+1))
done
注意点:
- 中括号内部两侧需要有空格,否则会被识别为命令
- 变量名最好加上双引号,否则当一个变量中包含空格的时候,会引发问题(双中括号的形式没有这个问题)
函数
函数和其他语言类似:
function test()
{
echo "参数个数:$#,参数列表:$@,第一个参数:$1"
return 0
}
# 调用
test 'a' 'b' 'c'
# 输出:参数个数:3,参数列表:a b c,第一个参数:a
echo $?
# 输出:0
需要注意的是,我们可以通过var=$(fun)
语法来获取函数的标准输出,返回值我们要通过$?
来获取。
判断函数是否存在
下面这些如果返回true代表命令存在:
if command -v foo >/dev/null 2>&1; then
if type foo >/dev/null 2>&1; then
if hash foo 2>/dev/null; then
bash中变量的作用域都是全局的,为避免函数中的变量被污染,可以在函数内部使用local关键字修饰变量。
bash中引入文件可以直接用source
命令。
数学计算
# 方法1
((var=1024/1024))
((c=$a+$b))
c=$((a+b))
# 方法2
let "var=1024/1024"
let c=a+b
# 方法3
var=$[1024+1024]
var=$[a+b]
var=$[$a+$b]
# 方法4(运算符之间必须有空格)
var=`expr $var + 1`
# 注意乘法这里的转义字符
var=$(expr $var \* 2)
# 借助外部程序
# bc
var=`echo "20*10"|bc`
# awk
var=`echo "20 2"|awk '{printf("%i",$1*$2)}'`
# awk的printf与c语言的类似,换为%f可以支持浮点运算
echo `echo 2014 2098 | awk '{printf("%f", $1/$2)}'`
对于shell的理解并不深刻,如有错误,欢迎指正。
参考链接
(完)
- 本文作者:吴泽辉
- 本文链接:https://mutex.top/posts/00a129c9/
- 发表日期:2019年8月28日
- 版权声明:本文章为原创,采用《知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议》进行许可