文章目录
  1. 1. 变量
    1. 1.1. 变量定义
    2. 1.2. read读取输入值
    3. 1.3. 环境变量
    4. 1.4. 参数变量
    5. 1.5. 读取返回码
  2. 2. 数学运算
    1. 2.1. let关键字
    2. 2.2. 使用(())
    3. 2.3. 使用$[]
    4. 2.4. 使用expr
    5. 2.5. 使用bc
  3. 3. 条件判断
    1. 3.1. if 语法
    2. 3.2. 判断表达式
      1. 3.2.1. 字符串比较
      2. 3.2.2. 算术比较
      3. 3.2.3. 文件条件测试
  4. 4. 循环结构
    1. 4.1. for 循环
    2. 4.2. while 循环
    3. 4.3. until语句
  5. 5. 函数
    1. 5.1. 函数的返回值
  6. 6. case语句
  7. 7. 外部命令/文件/语言的调用
  8. 8. 总结
  9. 9. 参考

Shell脚本是非常强的大一个脚本语言,但是不用会手生,所以在此记录Shell脚本的相应关键点,也做查字典用^_^

变量

变量定义

先来简单的看一下变量定义的规则

  1. Shell中,使用变量之前不需要事先声明,只是通过使用它们来创建它们;
  2. 在默认情况下,所有变量都被看做是字符串,并以字符串来存储;
  3. Shell变量是区分大小写的;
  4. 在赋值变量的时候等号两端不能有空格-_-

定义了变量之后,一定要加上$符号才能使用

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/bash

VAR1=HELLO
VAR2=MY NAME
VAR3="MY AGE"
VAR4 = IS

echo VAR1 #error 能输出 但不是输出该变量
echo $VAR1 #ok 正常读取变量并打印
echo $VAR2 #error 定义变量的值 用空格隔开了
echo $VAR3 #ok 作为一整个字符串
echo $VAR4 #error 变量定义的时候等号两端有空格

输出的结果为

./test.sh: line 2: NAME: command not found
./test.sh: line 4: VAR4: command not found
VAR1
HELLO

MY AGE

关于shell脚本的执行:shell基本一般是以.sh为后缀,然后在*unix系统下一般都是直接使用./[当前shell文件名] 的方式来执行,也可以使用全部经/[shell文件名]的方式来执行,并且需要注意的是 被执行的shell文件一定是有含有可执行权限了的,可以使用chmod命令来修改

还有另一个点就是在调用变量的时候 ,如果在双引号中直接使用$name任然可以识别,但是如果在单引号是就无法适用$name的方式来调用变量

read读取输入值

这个功能就像java中的readline来读取,使用方法为

1
2
3
4
5
6
7
#! /bin/bash

echo "whats your name?"
read NAME #在这里读取输入值到NAME变量中 ,这里如果不输入会停留在屏幕上
echo "webcome back" $NAME

exit 0

可以看到熟悉的结果为

whats your name?
tom
webcome back tom

环境变量

Shell脚本还提供能一些实用的环境变量

  1. $HOME:为当前用户所在的目录
  2. $PATH:当前用户所能方法的PATH变量
  3. $#:传递参数额个数 类似java中的args.length
  4. $$:Shell脚本的进程号,脚本程序通常会用它来生成一个唯一的临时文件。
1
2
3
4
5
6
7
8
9
#! /bin/bash

echo "当前用户所在的目录为" $HOME
echo "当前的执行目录为" $(pwd) #这个是访问当前的脚本的目录很实用
echo "当前用户所能访问的PATH为" $PATH
echo "当前参数的参数个数为" $# #这儿参数的格式是使用空格隔开的哦
echo "当前Shell脚本的进程号为" $$

exit 0

可以到看的结果是

yans-MacBook-Pro:Downloads yanyl$ ./hi.sh  hello world
当前用户所在的目录为 /Users/yanyl
当前的执行目录为 /Users/yanyl/Downloads
当前用户所能访问的PATH为 /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/yanyl/Program/apache-maven-3.2.5/bin:/Users/yanyl/Program/scala-2.10.4//bin
当前参数的参数个数为 2
当前Shell脚本的进程号为 43746

假如需要进入当前目录的父目录,可以使用$(dirname $(pwd))

参数变量

刚刚看到可以使用read关键字可以来读取输入变量,但是我们可能更加常用的是参数变量,也就是$#的个数,它的规则如下

  1. $#表示参数变量的个数
  2. $0表示当前的脚本名称
  3. $1,$2…$n表示依次能读取到的变量 但是如果参数变量不够,$i会被赋值为空
1
2
3
4
5
6
7
8
9
#! /bin/bash

echo "当前输入的参数变量的长度为" $#
echo "当前执行的Shell脚本为" $0
echo "当前输入的第一个参数为" $1
echo "当前输入的第二个参数为" $2
echo "当前的输入的第三个参数为" $3 #现在如果只传2个参数 这里将不会报错

exit 0

可以看到的结果为

yans-MacBook-Pro:Downloads yanyl$ ./hi.sh  hello world
当前输入的参数变量的长度为 2
当前执行的Shell脚本为 ./hi.sh
当前输入的第一个参数为 hello
当前输入的第二个参数为 world
当前的输入的第三个参数为

可以看到在Shell脚本中去读取参数变量还是很方便的,这样配合下面的条件判断以及循环就可以做很多事情了

读取返回码

一般的程序/命令在执行结束时都会返回一个 返回码,比如

  • javasystem.exit(-1)
  • pythonsys.exit(-1)
  • 还有上面Shell脚本中的最后一行exit 0

如果你不显式指定返回码,一般默认为0,表示正常退出,但是有时候显式的指定返回码是一个好习惯哦
这些程序在Shell中执行的,可以使用$?来读取上一个程序执行下来的脚本码

1
2
3
4
5
6
7
8
9
#! /bin/bash

du -s #执行的返回码一般为0
echo du -s的返回码为 $?

duu -s #这个命令故意输错
echo duu -s的返回码为 $?

exit 0

可以看到正确的结果为

28494656    .
du -s的返回码为 0
./hi.sh: line 6: duu: command not found
duu -s的返回码为 127

返回码配上if判断,就可以使用shell脚本自由得在各个语言以及命令中穿梭啦^_^

数学运算

在上一小节中说道,Shell中变量一般都是当字符串来处理,那我遇到数字运算该咋办呢??

可以先看

1
2
3
4
5
6
7
8
#! /bin/bash

a=1+2
b=$a+3
echo $a
echo $b

exit 0

结果却看到

1+2
1+2+3

那在Shell中解决这个问题大概有这么几种方法

let关键字

1
2
3
4
5
6
7
8
#! /bin/bash

let a=1+2
let b=$a+3
echo $a
echo $b

exit 0

输出的结果为

3
6

这个关键词大致需要注意以下几个点:

  • let只支持整数运算
  • let后面的运算部分有bash关键字时,需加双引号
  • 幂次方可以使用**符号

使用(())

1
2
3
4
5
6
7
8
#! /bin/bash

((a=1+2))
((b=$a+3))
echo $a
echo $b

exit 0

结果还是正确的

3
6

(())的用法与let完全相同

使用$[]

上面的效果需要这么写

1
2
a=$[1+2]
b=$[$a+3]

其余与上面两种限制大致相同

使用expr

关于这个方式是这么写的

1
2
a=`expr 1 + 2`
b=`expr $a \* 3` #需要转义

需要额外注意的有:

  • 运算符两端需要加空格 一定要记住。。。很容易失误
  • 对于|、&、<、<=、>=、>、*运算符号需要加上\进行转义

使用bc

这个终于是可以用于浮点数的运算了

1
2
3
4
5
6
7
8
#! /bin/bash

a=3.1415926
b=`echo "$a*2"|bc`
echo $a
echo $b

exit 0

可以看到结果

3.1415926
6.2831852

据说这里还有一个scale来设置精度,但是我设置了感觉木有效果-_-

条件判断

if 语法

Shell脚本中有两种书写if判断的语法

  • 使用test 关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #! /bin/bash

    # if test expression1 operation expression2
    if test 5 -gt 4; #这个最后的结尾可以加上:或者;
    then
    echo "ok,5>4"
    else
    echo "oh,no"
    fi #这个结束符号必须得加

    exit 0

    输出为

    ok,5>4
    
  • 使用[]关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #! /bin/bash

    # if [ expression1 operation expression2 ]
    if [ 5 -lt 4 ]; #注意[和]两端必须留空格 同时表达式两端都需要有空格
    then
    echo "ok,5>4"
    else
    echo "oh,no"
    fi

    exit 0

    输出为

    oh,no
    

如果还更加复杂的判断你可以使用elif继续增加条件表达式,但是别忘了加then

判断表达式

Shell中有三种判断表达式

字符串比较

字符串比较 结果
string1 = string2 如果两个字符串相同,也可用==结果就为真
string1 != string2 如果两个字符串不同,结果就为真
-n string 如果字符串不为空,则结果为真
-z string 如果字符串为一个空串(null),则结果为真

这里需要注意下,-n-z string比较时必须用双引号(“”)将变量引起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#! /bin/bash

a=5

if [ -n "$a" ] #注意要空括号来包住哦
then
echo exists
else
echo null
fi

if [ -n "$c" ]
then
echo exists
else
echo null
fi

exit 0

结果为

exists
null

算术比较

算术比较 结果
expression1 -eq expression2 如果两个表达式相等,则结果为真
expression1 -ne expression2 如果两个表达式不等,则结果为真
expression1 -gt expression2 如果expression1 大于expression2 ,则为真
expression1 -ge expression2 如果expression1 大于等于expression2 ,则为真
expression1 -lt expression2 如果expression1 小于expression2 ,则为真
expression1 -le expression2 如果expression1 小于等于expression2 ,则为真
!expression 表达式为假,则结果就为真;反之亦然

关于上面比较符号的快速记法如下:eq=equal,gt=great than,lt=less than,然后组合拼凑即可,如果觉得这样还是很难记,就可以像我一样,将这些符号记录下来,需要的时候来查表-_-

文件条件测试

文件条件测试 结果
-d file 如果文件是一个目录,则为真
-f file 如果文件是一个普通文件,则为真;也可以用来测试文件是否存在
-r file 如果文件可读,则结果为真
-s file 如果文件大小不为0,则结果为真
-w file 如果文件可写,则结果为真
-x file 如果文件可执行,则结果为真

这,真的是一个利民的测试

循环结构

for 循环

先来看一种经典C语法版的for

1
2
3
4
5
6
7
#! /bin/bash

for ((i=0;i<5;i++))
do
echo $i
done
exit 0

看输出,

0
1
2
3
4

还支持在外部控制步长

1
2
3
4
5
6
7
8
#! /bin/bash

for ((i=0;i<5;))
do
echo $i
i=$[$i+2]
done
exit 0

0
2
4

是不是感觉基本功能都有呀,就是写某些东西写起来奇怪点
是不是有一种莫名的熟悉感

另一种就是类似foreach的情况了,他的格式是这样的

1
2
3
4
for variable in values
do
statements
done

其中values 可能有的情况为:

  1. 使用linux命令输出的行作为迭代的输入:ls,seq,cat之类均可,其实就可以完成很强大的文件读取功能

    1
    2
    3
    4
    5
    6
    7
    #! /bin/bash

    for i in `head -n 5 words.dit`;do #words.dit 这是一个通用词表 每行一个词
    echo $i
    done

    exit 0

    可以看到通用词典中前5个词

    阿
    阿巴丹
    阿巴岛
    阿巴鸟
    阿巴伊达
    
  2. 使用$*可以来表示遍历传入的参数列表

    1
    2
    3
    4
    5
    6
    7
    #! /bin/bash

    for i in $*;do
    echo $i
    done

    exit 0

    来看个结果

    yans-MacBook-Pro:Downloads yanyl$ ./hi.sh  my name is tom
    my
    name
    is
    tom
    
  3. 还可以使用带空格的字符串 来进行按空格分隔输出

    1
    2
    3
    4
    5
    6
    7
    8
    #! /bin/bash

    a="yello red green"
    for i in $a;do
    echo $i
    done

    exit 0

    这样在一定程度上可以看成一个简易的数组

这里需要注意的是包含条件以及循环逻辑是双重括号,以及开始结果的doDone

while 循环

另一个常用的就是while循环了
他的结构是

1
2
3
4
while condition
do
statements
done

这个也是蛮好理解的,可以来看一个demo

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/bash

echo "please ent your password:"
read pwd

while [ "$pwd"x != "root"x ] #这里加x是为了防止啥也不输入直接回车产生的报错
do
echo "error,please try again:"
read pwd
done
echo "welcome here"

exit 0

看一下结果

please ent your password:
sha
error,please try again:

error,please try again:
root
welcome here

很有意思的一个哈~

until语句

这个语句与while的结构完全一样,只是使用了until关键字来代替了while,然后在条件为true的时候停止,正好与while相反

函数

Shell这么叼,能没有函数吗

1
2
3
4
[function] functon_name()
{
statements
}

上面是定义函数的结构,大致有以下几个要点

  1. 前面的function关键字可有可无,不过感觉还是加上去比较好,这样在代码里面比较好辨识
  2. 函数名后面的括号中不能带参数 取的参数是用过$1,$2…$n这样的方式来取的
  3. 调用的时候直接写函数名 不需要加括号
  4. 如果想传递参数的话 直接在调用后来加上参数列表 用空格隔开 (就是Shell的传参一样)
  5. 使用local关键字来定义函数体里面的局部变量
  6. 所以在函数调用必须在函数定义之后

先看一个小的demo

1
2
3
4
5
6
7
8
9
10
#! /bin/bash

function sayhi()
{
echo hi $1
}

sayhi tom #前面的sayhi是函数的调用 后面的tom是传参

exit 0

可以看到输出

hi tom

函数的返回值

关于Shell的返回值方式有两种

  1. 输出给主程序,他的结构为:

    1
    2
    3
    4
    5
    6
    function function_name()
    {
    echo $something #通过输出的方式来返回值
    }

    a=`function_name` 这种方式接收返回值

    看到的demo可以是这样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Press ENTER or type command to continue
    #! /bin/bash

    function sum()
    {
    echo $[$1+$2]
    }

    a=`sum 1 2`

    echo the sum is $a
    exit 0

    最终输出结果为

    the sum is 3
    
  2. 使用return作为返回码来返回值

    1
    2
    3
    4
    5
    6
    7
    function function_name()
    {
    return $ret #这里进行返回码的返回
    }

    function_name
    $? #在这里接收返回值

    一样再来一个demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #! /bin/bash

    function sum()
    {
    return $[$1+$2]
    }

    sum 1 2
    echo the sum is $?

    exit 0

    可以看到输出为

    the sum is 3
    

case语句

这里的case的与传统的switch有点像,但是又像scala中的match模式匹配的强大,
他的结构是这样的

1
2
3
4
5
case variable in
pattern [ | pattern] ...) statements;;
pattern [ | pattern] ...) statements;;
...
esac

来看这个强大的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#! /bin/bash

function match()
{
case $1 in
root ) echo this is password ;;
h* ) echo hi $1 ;; #使用通配符
yes | YES ) echo agree with me ;; #可以进行或操作
* ) echo everything is here;; #你可以理解为switch中的default
esac
}

match root
match hello
match YES
match Yes
exit 0

来看一下结果

this is password
hi hello
agree with me
everything is here

注意,这里一旦匹配中了一个之后就马上会停止匹配

外部命令/文件/语言的调用

Shell的另一个强大之处就是可以无缝的和外部的命令,文件,语言结合,去调用组织他们

  1. 外部命令:一般情况下可以直接写外部命令,如果要赋值的话得使用``括起来
  2. 外部文件:比如资源配置文件,profile文件之类的,可以直接使用source关键字的来执行
  3. 外部语言:比如java,python可以直接使用他们的java调用jar,java文件,也可以直接使用关键字来执行python文件

总结

  1. Shell很好很强大,得学习!!!
  2. 注意变量的字符串格式以及需要数学运算时的语法
  3. 注意变量赋值时等号两端一定不能有空格以及再取值时一定要加$
  4. 平常的控制结束符号别忘了,比如fi,doen,esac
  5. 忘了的时候来查查这个文件

参考


本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权

文章目录
  1. 1. 变量
    1. 1.1. 变量定义
    2. 1.2. read读取输入值
    3. 1.3. 环境变量
    4. 1.4. 参数变量
    5. 1.5. 读取返回码
  2. 2. 数学运算
    1. 2.1. let关键字
    2. 2.2. 使用(())
    3. 2.3. 使用$[]
    4. 2.4. 使用expr
    5. 2.5. 使用bc
  3. 3. 条件判断
    1. 3.1. if 语法
    2. 3.2. 判断表达式
      1. 3.2.1. 字符串比较
      2. 3.2.2. 算术比较
      3. 3.2.3. 文件条件测试
  4. 4. 循环结构
    1. 4.1. for 循环
    2. 4.2. while 循环
    3. 4.3. until语句
  5. 5. 函数
    1. 5.1. 函数的返回值
  6. 6. case语句
  7. 7. 外部命令/文件/语言的调用
  8. 8. 总结
  9. 9. 参考