脚本文件开头
在每个bash脚本的开头都使用#!
,这用来告诉系统此文件的执行需要指定一个解释器。#!
实际上是一个 2 字节的魔力数字,这是指定一个文件类型的特殊标记,换句话说, 在这里指是一个可执行的脚本(键入 man magic 来获得关于这个迷人话题的更多详细信息)。在#!
之后接着是一个路径名,这个路径名指定了一个解释脚本中命令的程序,这个程序可以是 shell,其它编程语言或任意一个通用程序。这个指定的程序从头开始解释并且执行脚本中的命令(从#!
行下边的一行开始),忽略注释。
如:
1 | #!/bin/sh |
上边每一个脚本头的行都指定了一个命令解释器,如果是/bin/sh
,那么就是默认shell(在 Linux 系统中默认是 Bash)。使用#!/bin/sh
,在大多数商业发行的 UNIX 上,默认是 Bourne shell,这将让你的脚本可以正常的运行在非 Linux 机器上,虽然这将会牺牲 Bash 一些独特的特征。脚本将与 POSIX 的 sh 标准相一致。
注意: #!
后边给出的路径名必须是正确的,否则将会出现一个错误消息,通常是"Command not found"
,这将是你运行这个脚本时所得到的唯一结果。当然 #!
也可以被忽略,不过这样你的脚本文件就只能是一些命令的集合,不能够使用 shell 内建的指令了。
脚本中的#!
行的最重要的任务就是命令解释器(sh 或者 bash)。因为这行是以#
开始的,当命令解释器执行这个脚本的时候,会把它作为一个注释行。当然,在这之前,这行语句已经完成了它的任务,就是调用命令解释器。
注:那些具有 UNIX 味道的脚本(基于 4.2BSD)需要一个 4 字节的魔法数字,在#!后边需要一个空格#! /bin/sh
。
转载自:
Shell 传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n
。n
代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
实例
以下实例我们向脚本传递三个参数,并分别输出,其中 $0
为执行的文件名。
脚本文件命名为test.sh
,内容如下:
1 | #!/bin/bash |
为脚本设置可执行权限,并执行脚本,输出结果如下所示:
1 | $ chmod +x test.sh |
在Shell中,脚本名称本身是$0
,我们这里就是“./test.sh”,剩下的依次是$1, $2,...,${10}, ${11}
,等等。$#
表示包括$0
在内的命令行参数的个数。$*
表示整个参数列表,不包括$0
,也就是说不包括文件名的参数列表。
处理参数的特殊字符
参数处理 | 说明 |
---|---|
$# |
表示包括$0 在内的命令行参数的个数 |
$* |
表示整个参数列表,不包括$0 ,也就是说不包括文件名的参数列表。如"$*" 用" 括起来的情况、以"$1 $2 … $n" 的形式输出所有参数。 |
$$ |
脚本运行的当前进程ID号 |
$! |
后台运行的最后一个进程的ID号 |
$@ |
与$* 相同,但是使用时加引号,并在引号中返回每个参数。如"$@" 用" 括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
$- |
显示Shell使用的当前选项,与set命令功能相同。 |
$? |
显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
$*
与 $@
区别:
相同点:都是引用所有参数。
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数
1、2、3
,则" * "
等价于"1 2 3"
(传递了一个参数),而"@"
等价于"1" "2" "3"
(传递了三个参数)。
例如:
1 | #!/bin/bash |
执行脚本,输出结果如下所示:
1 | $ chmod +x test.sh |
注意事项
在为shell脚本传递的参数中如果包含空格,应该使用单引号或者双引号将该参数括起来,以便于脚本将这个参数作为整体来接收。
在有参数时,可以使用对参数进行校验的方式处理以减少错误发生:
1 | if [ -n "$1" ]; then |
- 中括号 [] 与其中间的代码应该有空格隔开
转载自:
Shell 赋值
赋值记得等号前后不能有空格。
1 | #!/bin/bash |
Shell 脚本的if语句
if语法
if语句必须以单词fi终止。在if语句中漏写fi是最一般的错误。我自己有时也是这样。elif和else为可选项,如果语句中没有否则部分,那么就不需要elif和else部分。if语句可以有许多elif部分。
1 | if [ condition ] --注意括号两边有空格,condition 是个条件表达式 |
或者:
1 | if [ condition ]; then |
例如:
1 | if [ $a -gt $b ] |
if后的condition一定要是一个条件语句,其结果应该是true
或false
,虽然我们常常将1
认为是true
、0
认为是false
,但是这里的condition运算结果只能是true
或false
,否则,即使执行结果是1
或0
,都会认为condition这个条件是具备的,就不走其他分支了。
条件判断语句
条件测试的表达式:
[ expression ]
括号两端必须要有空格[[ expression ]]
括号两端必须要有空格组合测试条件:
-a
: and-o
: or!
: 非
整数比较:
-eq
测试两个整数是否相等-ne
测试两个整数是否不等-gt
测试一个数是否大于另一个数-lt
测试一个数是否小于另一个数-ge
大于或等于-le
小于或等于
命令间的逻辑关系
- 逻辑与:
&&
- 逻辑或:
||
- 逻辑与:
1 | if [ condition1 -a condition2 ] 或 if [ condition1 ] && [ condition2 ] |
- 字符串比较
==
等于 两边要有空格!=
不等>
大于<
小于
- 文件测试
-z
string 测试指定字符是否为空,空着真,非空为假-n
string 测试指定字符串是否为不空,空为假 非空为真-e
FILE 测试文件是否存在-f
file 测试文件是否为普通文件-d
file 测试指定路径是否为目录-r
file 测试文件对当前用户是否可读-w
file 测试文件对当前用户是否可写-x
file 测试文件对当前用户是都可执行-z
是否为空 为空则为真-a
是否不空
If语句进行判断是否为空:
[ "$name” = "" ]
等同于 [ ! "$name" ]
和 [ -z "$name" ]
示例脚本
写一段脚本,输入一个测验成绩,根据下面的标准,输出他的评分成绩(A-F)
A: 90–100
B: 80–89
C: 70–79
D: 60–69
F: <60
脚本文件命名为grade.sh
,内容如下:
1 | #/bin/bash |
执行结果:
1 | [root@lovelace if]# ./grade.sh |
转载自
在 Shell 脚本中调用另一个 Shell 脚本的三种方式
先来说一下主要以下有几种方式:
fork: 如果脚本有执行权限的话,
path/to/foo.sh
。如果没有,sh path/to/foo.sh
。exec:
exec path/to/foo.sh
source:
source path/to/foo.sh
fork
fork 是最普通的, 就是直接在脚本里面用 path/to/foo.sh 来调用 foo.sh
这个脚本,比如如果是 foo.sh
在当前目录下,就是 ./foo.sh
。运行的时候 terminal 会新开一个子 Shell 执行脚本 foo.sh
,子 Shell 执行的时候, 父 Shell 还在。子 Shell 执行完毕后返回父 Shell。 子 Shell 从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回父 Shell。
exec
exec 与 fork 不同,不需要新开一个子 Shell 来执行被调用的脚本. 被调用的脚本与父脚本在同一个 Shell 内执行。但是使用 exec 调用一个新脚本以后, 父脚本中 exec 行之后的内容就不会再执行了。这是 exec 和 source 的区别.
source
与 fork 的区别是不新开一个子 Shell 来执行被调用的脚本,而是在同一个 Shell 中执行. 所以被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。
例子
其实从命名上可以感知到其中的细微区别,下面通过两个脚本来体会三种调用方式的不同:
第一个脚本,我们命名为 1.sh
:
1 | #!/usr/bin/env bash |
第二个脚本,我们命名为 2.sh
:
1 | #!/usr/bin/env bash |
注:这两个脚本中的参数 $$
用于返回脚本的 PID , 也就是进程 ID。这个例子是想通过显示 PID 判断两个脚本是分开执行还是同一进程里执行,也就是是否有新开子 Shell。当执行完脚本 2.sh
后,脚本 1.sh
后面的内容是否还执行。
chmod +x 1.sh 2.sh
给两个脚本加上可执行权限后执行情况:
fork
fork 方式可以看出,两个脚本都执行了,运行顺序为1-2-1,从两者的PID值(1.sh PID=82266, 2.sh PID=82267),可以看出,两个脚本是分成两个进程运行的。
exec
exec 方式运行的结果是,2.sh 执行完成后,不再回到 1.sh。运行顺序为 1-2。从pid值看,两者是在同一进程 PID=82287 中运行的。
source
source方式的结果是两者在同一进程里运行。该方式相当于把两个脚本先合并再运行。
总结
Command | Explanation |
---|---|
fork | 新开一个子 Shell 执行,子 Shell 可以从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回给父 Shell。 |
exec | 在同一个 Shell 内执行,但是父脚本中 exec 行之后的内容就不会再执行了 |
source | 在同一个 Shell 中执行,在被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用,相当于合并两个脚本在执行。 |
转载自
shell中局部变量及local命令
local一般用于局部变量声明,多在在函数内部使用。
(1)shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止。
(2)shell函数定义的变量默认是global的,其作用域从“函数被调用时执行变量定义的地方”开始,到shell结束或被显示删除处为止。函数定义的变量可以被显示定义成local的,其作用域局限于函数内。但请注意,函数的参数是local的。
(3)如果同名,Shell函数定义的local变量会屏蔽脚本定义的global变量。
1 | #!/bin/bash |
转载自