解释器
#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器
# 以下两种方式都可以指定 shell 解释器为 bash,# 第二种方式更好,系统会自动在 PATH 环境变量中查找你指定的程序#!/bin/bash#!/usr/bin/env bash
注释
- 单行注释 - 以
#开头,到行尾结束。 - 多行注释 - 以
:<<EOF开头,到EOF结束。
基本语法
echo
输出普通字符串:
echo "hello, world"# Output: hello, world
输出含特殊字符的字符串:
echo "hello, \"zp\""# Output: hello, "zp"
输出含变量的字符串:
name=zpecho "hello, \"${name}\""# Output: hello, "zp"
输出含换行符的字符串:
# 输出含换行符的字符串echo "YES\nNO"# Output: YES\nNOecho -e "YES\nNO" # -e 开启转义# Output:# YES# NO
输出含不换行符的字符串:
echo "YES"echo "NO"# Output:# YES# NOecho -e "YES\c" # -e 开启转义 \c 不换行echo "NO"# Output:# YESNO
输出重定向至文件
echo "test" > test.txt
输出执行结果
echo `pwd`# Output:(当前目录路径)
变量
变量类型
- 局部变量
仅在某个脚本内部有效的变量
- 环境变量
可以使用export创建环境变量
系统自带环境变量
$HOME |
当前用户的用户目录 |
|---|---|
$PATH |
用分号分隔的目录列表,shell 会到这些目录中查找命令 |
$PWD |
当前工作目录 |
$RANDOM |
0 到 32767 之间的整数 |
$UID |
数值类型,当前用户的用户 ID |
声明和访问变量
访问变量的语法形式为:${var} 和 $var
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。
word="hello"echo ${word}
只读变量
rword="hello"echo ${rword}readonly rword# rword="bye" # 如果放开注释,执行时会报错
删除变量
unset dword # 删除变量echo ${dword}# Output: (空)
字符串
shell 字符串可以用单引号 '',也可以用双引号 “”,也可以不用引号。
- 单引号的特点
- 单引号里不识别变量
- 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。
- 双引号的特点
- 双引号里识别变量
- 双引号里可以出现转义字符
获取字符串长度
text="12345"echo ${#text}# Output:# 5
截取字符串长度
text="12345"echo ${text:2:2}# Output:# 34
查找子字符串
#!/usr/bin/env bashtext="hello"echo `expr index "${text}" ll`# Execute: ./str-demo5.sh# Output:# 3
查找 ll 子字符在 hello 字符串中的起始位置。
数组
创建数组
# 两种方式nums=([2]=2 [0]=0 [1]=1)colors=(red yellow "dark blue")
访问数组
最好使用{}
echo ${nums[1]}# Output: 1# 访问所有数据echo ${colors[*]}# Output: red yellow dark blueecho ${colors[@]}# Output: red yellow dark blue
访问数组长度
echo ${#nums[*]}# Output:# 3
增加元素
colors=(white "${colors[@]}" green black)
删除元素
unset nums[0]
遍历数组
for i in ${my_arry[@]};doecho $idone
运算符
算数运算符
执行方式: val=expr ${x} + ${y} (这种方式需要转义)
运算符: + - * / %
或者
在算数表达式中,使用变量无需带上$前缀:
x=4y=7echo $(( x + y )) ### 11echo $(( ++x + y++ )) ### 12echo $(( x + y )) ### 13#另一种方式echo $[ $x + $y ]不能使用 [[ $x + $y ]]
条件运算符
字符比较
| == | 相等。用于比较两个数字,相同则返回 true。 | [ $x == $y ]返回 false。 |
|---|---|---|
| != | 不相等。用于比较两个数字,不相同则返回 true。 | [ $x != $y ]返回 true。 |
数值比较
-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。 |
:::info
注意:只支持整数,不支持浮点数,不支持字符比较
条件表达式要放在方括号之间,并且要有空格
例如: [ $x == $y ]是错误的,必须写成 [ $x == $y ]。
:::
if [[ ${x} == ${y} ]]thenecho "${x} = ${y}"fiif [[ ${x} != ${y} ]]thenecho "${x} != ${y}"fiif [[ ${x} -ge ${y} ]]; thenecho "${x} -ge ${y}: x 大于或等于 y"elseecho "${x} -ge ${y}: x 小于 y"fiif [[ ${x} -le ${y} ]]; thenecho "${x} -le ${y}: x 小于或等于 y"elseecho "${x} -le ${y}: x 大于 y"fi
逻辑运算符
! |
非运算,表达式为 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。 |
&& |
逻辑的 AND | [[ ${x} -lt 100 && ${y} -gt 100 ]]返回 false |
|---|---|---|
|| |
逻辑的 OR | [[ ${x} -lt 100 || ${y} -gt 100 ]]返回 true |
if [[ ${x} -lt 100 && ${y} -gt 100 ]]thenecho "${x} -lt 100 && ${y} -gt 100 返回 true"elseecho "${x} -lt 100 && ${y} -gt 100 返回 false"fiif [[ ${x} -lt 100 || ${y} -gt 100 ]]thenecho "${x} -lt 100 || ${y} -gt 100 返回 true"elseecho "${x} -lt 100 || ${y} -gt 100 返回 false"fi
字符串运算符
-z |
检测字符串长度是否为 0,为 0 返回 true。 | [ -z $a ]返回 false。 |
|---|---|---|
-n |
检测字符串长度是否为 0,不为 0 返回 true。 | [ -n $a ]返回 true。 |
str |
检测字符串是否为空,不为空返回 true。 | [ $a ]返回 true。 |
文件测试运算符
| 操作符 | 说明 | 举例 |
|---|---|---|
| -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。 |
控制语句
[[ ]](sh中是[ ])包起来的表达式被称作 检测命令 或 基元。
if
形式:
if command
then
commands
fi
if 语句会运行if后面的command,如果该command退出码是0(成功),then部分就会被执行
例如:
if pwd; thenecho somefi输出:/home/delta/edgex-mongo/scriptsome
如需进行条件判断,需要借助test命令或者[ ]
单行:
if [[ 表达式 ]]; then … ; fi
多行:
if [[表达式]]; then … elif [[表达式]]; then … else … fi
[ condition ]、[[ condition ]]、(( condition )) 区别
[ condition ] : 可以进行数值、字符、文件比较判断,但对于 “>” 、”<”符号需要转义
[[ condition ]] : 可以进行数值、字符、文件比较判断,同时 “>” 、”<”符号不需要转义,但只能不支持部分数值运算符号(如:val++)
(( condition )):可以进行数值、字符、文件比较判断,支持高级数值运算表达式,支持==不支持-eq等符号
总结:推荐使用 [[ condition ]],对于需要使用高级运算符号的情景,再使用(( condition ))
例:
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi# 写成多行x=10y=20if [[ ${x} > ${y} ]]; thenecho "${x} > ${y}"elif [[ ${x} < ${y} ]]; thenecho "${x} < ${y}"elseecho "${x} = ${y}"fi# 比较字符串#如果$A等于a*(字符匹配),那么结果为trueif [ "$A" == "a*" ];thenecho "[ == ]"fi
case
case 值 in模式1)command1command2command3;;模式2)command1command2command3;;*)command1command2command3;;esac
例
case $x in1)echo value is 1;;2)echo value is 2;;*)echo other value;;esac
for
for arg in elem1 elem2 ... elemNdo### 语句done
elem之间使用空格分割
遍历数字
for i in {1..5}; do echo $i; done
{} 之间使用 ..
遍历数组
arr=( a b c d ) for s in ${arr[*]}; do echo $s; done
遍历文件夹
DIR=/home/zpfor FILE in ${DIR}/*.sh; domv "$FILE" "${DIR}/scripts"done
类C用法
for (( i = 0; i < 10; i++ )); doecho $idone
while
while [[ condition ]]do### 语句done
until
until循环跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为 假 就一直执行循环:
until [[ ${x} -ge 5 ]]; doecho ${x}x=`expr ${x} + 1`done
函数
[ function ] funname [()] {action;[return int;]}
- function关键字可省略
- 函数返回值 - return 返回函数返回值,返回值类型只能为整数(0-255)。如果不加 return 语句,shell 默认将以最后一条命令的运行结果,作为函数返回值。
- 函数返回值在调用该函数后通过 $? 来获得。
简写
funname(){
...
}
位置参数
位置参数是在调用一个函数并传给它参数时创建的变量。
| 变量 | 描述 |
|---|---|
$0 |
脚本名称 |
$1 … $9 |
第 1 个到第 9 个参数列表 |
${10} … ${N} |
第 10 个到 N 个参数列表 |
$*or $@ |
除了$0外的所有位置参数 |
$# |
不包括$0在内的位置参数的个数 |
$FUNCNAME |
函数名称(仅在函数内部有值) |
#!/usr/bin/env bash
x=0
if [[ -n $1 ]]; then
echo "第一个参数为:$1"
x=$1
else
echo "第一个参数为空"
fi
y=0
if [[ -n $2 ]]; then
echo "第二个参数为:$2"
y=$2
else
echo "第二个参数为空"
fi
paramsFunction(){
echo "函数第一个入参:$1"
echo "函数第二个入参:$2"
}
paramsFunction ${x} ${y}
===================================
$ ./function-demo2.sh 10 20
第一个参数为:10
第二个参数为:20
函数第一个入参:10
函数第二个入参:20
:::info $ 既可以获取传入脚本的参数,也可以获取传入函数的参数 :::
处理参数
$$ |
脚本运行的当前进程 ID 号 |
|---|---|
$! |
后台运行的最后一个进程的 ID 号 |
$- |
返回 Shell 使用的当前选项,与 set 命令功能相同。 |
$? |
函数返回值 |
shell拓展
大括号
echo beg{i,a,u}n ### begin began begun echo {0..5} ### 0 1 2 3 4 5 echo {00..8..2} ### 00 02 04 06 08
命令置换
echo
docker dateecho $(date)
单引号和双引号
单引号和双引号之间有很重要的区别。在双引号中,变量引用或者命令置换是会被展开的。在单引号中是不会的。举个例子:
echo "Your home: $HOME" ### Your home: /Users/<username>
echo 'Your home: $HOME' ### Your home: $HOME
当局部变量和环境变量包含空格时,它们在引号中的扩展要格外注意。随便举个例子,假如我们用echo来输出用户的输入:
INPUT="A string with strange whitespace."
echo $INPUT ### A string with strange whitespace.
echo "$INPUT" ### A string with strange whitespace.
调用第一个echo时给了它 5 个单独的参数 —— $INPUT 被分成了单独的词,echo在每个词之间打印了一个空格。第二种情况,调用echo时只给了它一个参数(整个$INPUT 的值,包括其中的空格)。
来看一个更严肃的例子:
FILE="Favorite Things.txt"
cat $FILE ### 尝试输出两个文件: `Favorite` 和 `Things.txt`
cat "$FILE" ### 输出一个文件: `Favorite Things.txt`
尽管这个问题可以通过把 FILE 重命名成Favorite-Things.txt来解决,但是,假如这个值来自某个环境变量,来自一个位置参数,或者来自其它命令(find, cat, 等等)呢。因此,如果输入 可能 包含空格,务必要用引号把表达式包起来。
流与重定向
| 代码 | 描述符 | 描述 |
|---|---|---|
0 |
stdin |
标准输入 |
1 |
stdout |
标准输出 |
2 |
stderr |
标准错误输出 |
| Operator | Description |
|---|---|
> |
重定向输出 |
&> |
重定向输出和错误输出 |
&>> |
以附加的形式重定向输出和错误输出 |
< |
重定向输入,输出到终端 |
<< |
Here 文档 语法 |
<<< |
Here 字符串 |
### ls的结果将会被写到list.txt中
ls -l > list.txt
### 将输出附加到list.txt中
ls -a >> list.txt
### 所有的错误信息会被写到errors.txt中
grep da * 2> errors.txt
### 从errors.txt中读取输入
ls -l < errors.txt
command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃
