Shell 语法

变量

声明、使用、只读、删除

myUrl="runoob.com"

echo $myUrl ${myUrl}

readonly myUrl

unset myUrl

1、定义变量时,不加 $ ,变量名和等号之间 不能有空格
2、使用变量,变量名前面加 $
3、{ }可选,加不加都行,加花括号是为了帮助解释器识别变量的边界
4、unset命令,不能删除只读变量

字符串(拼接、长度、截取、查找)

字符串可以用单引号,也可以用双引号,也可以不用引号。

单双引号的区别

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

双引号的优点:
①双引号里可以有变量;
②双引号里可以出现转义字符

拼接字符串

your_name="runoob"

# 使用双引号拼接
greeting="hello, "$your_name" !"

greeting_1="hello, ${your_name} !"

# 使用单引号拼接
greeting_2='hello, '$your_name' !'

# hello, runoob !

错误拼接

greeting_3='hello, ${your_name} !'

# hello, ${your_name}  !

获取字符串长度

echo ${#your_name} # 输出 6

提取/截取子字符串 :从字符串第 2 个字符开始截取 4 个字符:

echo ${your_name:1:4}  # 输出 unoo

查找子字符串 :查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):

echo `expr index "$your_name" io`  # 输出 4

数组(定义、读取、长度)

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

定义数组

在 Shell 中,用 括号 来表示数组,数组元素用 "空格" 符号分割开。定义数组的一般形式为:

arrays=(value0 value1 value2 value3)

array=(
value0
value1
value2
value3
)

单独定义,可以不使用连续的下标,而且下标的范围没有限制

array[5]=value

读取数组

${array[0]}

使用 @ 符号可以获取数组中的所有元素

echo ${array[@]}

获取数组的长度

length=${#array[@]}

length=${#array[*]}

取得数组单个元素的长度

lengthn=${#array[n]}

注释

单行注释

# 开头,会被解释器忽略。

多行注释

:<

EOF 也可以使用其他符号:

:<<'
注释内容...
'
:<

非常规方法:可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
awk 是一个优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一。awk 是三位创始人的首字母。
expr 是一款表达式计算工具,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串。

表达式和运算符之间要有空格,完整的表达式要被 `` 包含。

val=`expr 2 + 2`

算术运算符

+-*/%===!=

1、条件表达式要放在方括号之间,并且要有空格,例如: [ $a == $b ]

2、乘号(*)前边必须加反斜杠(\)才能实现乘法运算;val=`expr \$a \* $b`

3、在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 。

关系运算符

命令 说明
-eq 相等
-ne 不等
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于

布尔运算符

! 非 、-o 或 、-a

逻辑运算符

&&||

字符串运算符

命令 说明
= 相等
!= 不等
-z 长度为0
-n 长度不为0
$ 字符串不为空
[ $a = $b ]

[ -z $a ] 

文件测试运算符

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

其他检查符:

-S file 判断某文件是否 socket
-L file 检测文件是否存在并且是一个符号链接
[ -b $file ]
...

echo、printf、test

echo

echo "It is a test"

echo It is a tests

echo "It is a test" > myfile    //显示结果定向至文件

echo '$name\"'    //原样输出字符串,不进行转义或取变量(用单引号)

echo `date`    //显示命令执行结果

echo -e "OK! \n"  //显示换行

echo -e "OK! \c"  //显示不换行

显示变量(从命令行里获取值)

read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

#!/bin/sh

read name 

echo "$name It is a test"

以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:

sh test.sh

OK                     #标准输入

OK It is a test        #输出

printf

printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf "Hello, Shell\n"
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 
# 郭靖     男      66.12

%s、%c、%d、%f 都是格式替代符

%-10s 指一个宽度为10个字符( -表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中.2指保留2位小数。

#!/bin/bash
 
# format-string为双引号
printf "%d %s\n" 1 "abc"
#1 abc

# 单引号与双引号效果一样 
printf '%d %s\n' 1 "abc"
#1 abc

# 没有引号也可以输出
printf %s abcdef
#abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
#abcdef

printf "%s\n" abc def
#abc
#def

printf "%s %s %s\n" a b c d e f g h i j    

#a b c
#d e f
#g h i
#j  

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
# and 0

printf的转义序列

命令 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

test

数值测试

if test $[num1] -eq $[num2]
...
a=5
b=6

result=$[a+b] # 代码中的 [] 执行基本的`算数运算`, 等号两边不能有空格

字符串测试

if test $num1 = $num2
...

文件测试

if test -e ./bash
...

逻辑操作符

Shell提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:!最高,-a次之,-o最低

if test -e ./notFile -o -e ./bash
...

流程控制

和Java、PHP等语言不一样,sh的流程控制不可为空。在sh/bash里,如果else分支没有语句执行,就不要写else

if else

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

写成一行(适用于终端命令提示符):

if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi

if else语句经常与test命令结合使用,如下所示:

if test $[num1] -eq $[num2]
then
    echo '两个数字相等!'
else
    echo '两个数字不相等!'
fi

for 循环

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

写成一行

for var in item1 item2 ... itemN; do command1; command2… done;
for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

for 其他用法

对于习惯其他语言 for 循环的朋友来说可能有点别扭。

for((assignment;condition:next));
do
    command_1;
    commond_..;
done

这里的 for 循环与 C 中的相似,但并不完全相同。

通常情况下 shell 变量调用需要加 $,但是 for 的 (()) 中不需要

#!/bin/bash
for((i=1;i<=5;i++));do
    echo "这是第 $i 次调用";
done;

while 语句

#!/bin/bash
int=1
while(( $int<=5 ))
do
    echo $int
    let "int++"
done

以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。

while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按结束循环。

echo '按下  退出'
echo -n '输入你最喜欢的网站名: '

while read FILM
do
    echo "是的!$FILM 是一个好网站"
done

无限循环

while :
do
    command
done
while true
do
    command
done
for (( ; ; ))

until 循环

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

语法:

until condition
do
    command
done

实例:

#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done

case

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac

跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue

break命令

break命令允许跳出所有循环(终止执行后面的所有循环)。

#!/bin/bash
while :
do
    echo -n "输入 1 到 5 之间的数字:"
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你输入的数字为 $aNum!"
        ;;
        *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
            break
        ;;
    esac
done

continue

continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

#!/bin/bash
while :
do
    echo -n "输入 1 到 5 之间的数字: "
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你输入的数字为 $aNum!"
        ;;
        *) echo "你输入的数字不是 1 到 5 之间的!"
            continue
            echo "游戏结束"
        ;;
    esac
done

运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。

case ... esac

case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。

case 值 in
模式1)
    command1
    ...
    ;;
模式2)
    command1
    ...
    ;;
*)
    command1
    ...
    ;;
esac

case 后为取值,值可以为变量或常数。

值后为关键字 in,接下来是匹配的各种模式,每一模式最后必须以右括号结束,模式支持正则表达式。

#!/bin/sh

site="runoob"

case "$site" in
   "runoob") echo "菜鸟教程"
   ;;
   "google") echo "Google 搜索"
   ;;
   "taobao") echo "淘宝网"
   ;;
esac

函数

[ function ] funname [()]

{

    action;

    [return int;]

}

1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255),还可以通过echo 直接返回。
3、注意,shell中通过return返回是有限制的,最大返回255,超过255,则从0开始计算

#!/bin/bash

demoFun(){
    echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"

下面定义一个带有return语句的函数:

#!/bin/bash

funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum 和 $anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
函数返回值在调用该函数后通过 $? 来获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

函数参数

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

带参数的函数示例:

#!/bin/bash

funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第十个参数为 $10 !"        //会输出:第十个参数为 10 ! 而非第十个
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

注意,$10 不能获取第十个参数,获取第十个参数需要 ${10}。当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

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

补充:

1、$? 仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 $? 获得。

2、函数与命令的执行结果可以作为条件语句使用。要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false

更多

shell和shell脚本

你可能感兴趣的:(shell-script,shell)