shell 语法

1. 注释

# 这是一个单行注释

:<<EOF
这是多行注释
这是多行注释
这是多行注释
EOF

:<<'
这是多行注释
这是多行注释
这是多行注释
'

:<<!
这是多行注释
这是多行注释
这是多行注释
!

2. 变量

定义变量时,变量名不加美元符号,**NOTE: 变量名和等号之间不能有空格.**使用时变量前加美元符号.可加{}分离边界

your_name="tom"
echo $your_name

myUrl="https://www.google.com"
readonly myUrl # 只读变量

unset variable_name # 删除变量

3. 字符串

字符串可用单引号或双引号,也可不用引号.

单引号限制:

  • 单引号里的任何字符都会原样输出, 单引号字符串中的变量是无效的;
  • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行), 但可成对出现, 作为字符串拼接使用。

双引号的优点:

  • 双引号里可以有变量
  • 双引号里可以出现转义字符
str='this is a string'
str="Hello, I know you are \"$your_name\"! \n"
echo ${#str} # 获取字符串长度
echo ${str:1:4} 提取子字符串
echo `expr index $str io` # 查找子字符串

4. 数组

bash支持一维数组(不支持多维数组), 并且没有限定数组的大小。

array_name=(value0 value1 value2 value3)
echo array_name[0]  
echo array_name[@] #获取所有元素
length=${#array_name[@]} #  取得数组元素的个数
length=${#array_name[*]} #  取得数组元素的个数
lengthn=${#array_name[n]} # 取得数组单个元素的长度

5. 传递参数

执行 Shell 脚本时, 向脚本传递参数, 脚本内获取参数的格式为:$nn 代表一个数字

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同, 但是使用时加引号, 并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$-显示Shell使用的当前选项, 与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误, 其他任何值表明有错误。

$* 与 $@ 区别:

  • 相同点:都是引用所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3, , 则 " * " 等价于 "1 2 3"(传递了一个参数), 而 "@" 等价于 "1" "2" "3"(传递了三个参数)。

6. 运算符

Shell 和其他编程语言一样, 支持多种运算符, 包括:

  • 算数运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

a. 算术运算符

原生bash不支持简单的数学运算, 但是可以通过其他命令来实现, 例如 awkexpr, expr 最常用。

两点注意:

  • 表达式和运算符之间要有空格, 例如 2+2 是不对的, 必须写成 2 + 2, 这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 **`` ** 包含, 注意这个字符不是常用的单引号, 在 Esc 键下边。
运算符说明举例
+加法expr $a + $b 结果为 30。
-减法expr $a - $b 结果为 -10。
*乘法expr $a \* $b 结果为 200。
/除法expr $b / $a 结果为 2。
%取余expr $b % $a 结果为 0。
=赋值a=$b 将把变量 b 的值赋给 a。
==相等。用于比较两个数字, 相同则返回 true。[ $a == $b ] 返回 false。
!=不相等。用于比较两个数字, 不相同则返回 true。[ $a != $b ] 返回 true。

**注意:**条件表达式要放在方括号之间, 并且要有空格, 例如: [$a==$b] 是错误的, 必须写成 [ $a == $b ]

b. 关系运算符

关系运算符只支持数字, 不支持字符串, 除非字符串的值是数字

运算符说明举例
-eq检测两个数是否相等, 相等返回 true。[ $a -eq $b ] 返回 false。
-ne检测两个数是否不相等, 不相等返回 true。[ $a -ne $b ] 返回 true。
-gt检测左边的数是否大于右边的, 如果是, 则返回 true。[ $a -gt $b ] 返回 false。
-lt检测左边的数是否小于右边的, 如果是, 则返回 true。[ $a -lt $b ] 返回 true。
-ge检测左边的数是否大于等于右边的, 如果是, 则返回 true。[ $a -ge $b ] 返回 false。
-le检测左边的数是否小于等于右边的, 如果是, 则返回 true。[ $a -le $b ] 返回 true。

c. 布尔运算符

运算符说明举例
!非运算, 表达式为 true 则返回 false, 否则返回 true。[ ! false ] 返回 true。
-o或运算, 有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算, 两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

d. 逻辑运算符

运算符说明举例
&&逻辑的 AND[[ $a -lt 100 && $b -gt 100 ]] 返回 false
||逻辑的 OR[[ $a -lt 100 || $b -gt 100 ]] 返回 true

e. 字符串运算符

运算符说明举例
=检测两个字符串是否相等, 相等返回 true。[ $a = $b ] 返回 false。
!=检测两个字符串是否相等, 不相等返回 true。[ $a != $b ] 返回 true。
-z检测字符串长度是否为0, 为0返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否不为 0, 不为 0 返回 true。[ -n "$a" ] 返回 true。
$检测字符串是否为空, 不为空返回 true。[ $a ] 返回 true。

f. 文件测试运算符

操作符说明举例
-b file检测文件是否是块设备文件, 如果是, 则返回 true。[ -b $file ] 返回 false。
-c file检测文件是否是字符设备文件, 如果是, 则返回 true。[ -c $file ] 返回 false。
-d file检测文件是否是目录, 如果是, 则返回 true。[ -d $file ] 返回 false。
-f file检测文件是否是普通文件(既不是目录, 也不是设备文件), 如果是, 则返回 true。[ -f $file ] 返回 true。
-g file检测文件是否设置了 SGID 位, 如果是, 则返回 true。[ -g $file ] 返回 false。
-k file检测文件是否设置了粘着位(Sticky Bit), 如果是, 则返回 true。[ -k $file ] 返回 false。
-p file检测文件是否是有名管道, 如果是, 则返回 true。[ -p $file ] 返回 false。
-u file检测文件是否设置了 SUID 位, 如果是, 则返回 true。[ -u $file ] 返回 false。
-r file检测文件是否可读, 如果是, 则返回 true。[ -r $file ] 返回 true。
-w file检测文件是否可写, 如果是, 则返回 true。[ -w $file ] 返回 true。
-x file检测文件是否可执行, 如果是, 则返回 true。[ -x $file ] 返回 true。
-s file检测文件是否为空(文件大小是否大于0), 不为空返回 true。[ -s $file ] 返回 true。
-e file检测文件(包括目录)是否存在, 如果是, 则返回 true。[ -e $file ] 返回 true。

其他检查符:

  • -S: 判断某文件是否 socket。
  • -L: 检测文件是否存在并且是一个符号链接。

7. 流程控制

流程控制可用breakcontinue 控制循环

# if 语句
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi
# for 循环
for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done
# 或
for var in item1 item2 ... itemN; do command1; command2… done;

# while  循环
while condition
do
    command
done

# 无限循环
while :
do
    command
done
# 或
while true
do
    command
done
# 或
for (( ; ; ))

# until 循环
until condition
do
    command
done

# case 多选择语句
case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

8. 函数

  • 函数返回值在调用该函数后通过 $? 来获得

  • 调用函数时可以向其传递参数。在函数体内部, 通过 $n 的形式来获取参数的值, 例如, $1表示第一个参数, $2表示第二个参数

参数处理说明
$#传递到脚本或函数的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同, 但是使用时加引号, 并在引号中返回每个参数。
$-显示Shell使用的当前选项, 与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误, 其他任何值表明有错误
[ function ] funname [()] {
    action;
    [return int;]
}

9. 输入输出重定向

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

如果希望屏蔽 stdout 和 stderr,可以这么写command > /dev/null 2>&1.

**注意:**0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR.)这里的 2> 之间不可以有空格, 2> 是一体的时候才表示错误输出。

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN), 1 是标准输出(STDOUT), 2 是标准错误输出(STDERR)。

10. 文件包含

. filename   # 注意点号(.)和文件名中间有一空格
或
source filename
# 例
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh