什么是Shell?
简单的shell脚本
新建一个 test.sh, vi 输入下面内容
#!/bin/bash # This is a very simple example echo Hello World
1. #! 这一行表明,不管用户选择的是那种交互式shell,该脚本需要使用bash shell来运行。
2. # 就是 BASH 程序的注释,在 BASH 程序中从“#”号(注意:后面紧接着是“!”号的除外)开始到行尾的多有部分均被看作是程序的注释。变量
任何编程语言都会用到变量。你可以使用下面的语句来定义一个变量:
并按下面的格式来引用这个变量:
$X
更具体的说,$X表示变量X的值。关于语义方面有如下几点需要注意:
- 等于号两边不可以有空格!例如,下面的变量声明是错误的 :
X = hello
- 在展示的例子中,引号并不都是必须的。只有当变量值包含空格时才需要加上引号。例如:
X=hello world # 错误 X="hello world" # 正确
关于单引号和双引号:
基本上来说,变量名会在双引号中展开,单引号中则不会。
如果你不需要引用变量值,那么使用单引号可以很直观的输出你期望的结果。
也可以在双引号中使用转义字符
#!/bin/bash # this is simple example /bin/echo -n '$USER=' # -n选项表示阻止echo换行,mac下echo -n不生效改为/bin/echo -n echo "$USER" echo "\$USER=$USER" # 该命令等价于上面的两行命令
输出结果为:

X=ABC echo "$Xabc"
这是由于shell以为我们想要打印变量Xabc的值,实际上却没有这个变量。为了解决这种问题可以用大括号将变量名包围起来,从而避免其他字符的影响。
X=ABC echo "${X}abc"
输出结果为:
![]()
在执行中展开变量:
#!/bin/bash LS="ls" AL="-al" echo "$HOME" echo "$LS $AL $HOME" $LS $AL $HOME
执行结果如下:

条件判断if/then/elif
条件判断语法如下:
if condition then statement1 statement2 ...... fi
当指定条件不满足时,可以通过else来指定其他执行动作。
if condition then statement1 statement2 ...... else statement3 fi
当if条件不满足时,可以添加多个elif来检查其他条件是否满足。
if condition1 then statement1 statement2 ...... elif condition2 then statement3 statement4 ...... elif condition3 then statement5 statement6 ...... fi
Test命令与操作符
条件判断中的命令几乎都是test命令。test根据测试条件通过或失败来返回true或false(更准确的说是返回0或非0值)。
test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
test operand1 operator operand2
通常这么简写:
[ operand1 operator operand2 ]
例:
#!/bin/bash X=3 Y=4 empty_string="" if [ $X -lt $Y ] then echo "X=${X},which is smaller than Y=${Y}" fi if [ -n "$empty_string" ] then echo "this is not empty String" else echo "this is empty Stirng" fi
输出结果:
![]()
Test操作符简介
数值测试
| 参数 | 说明 |
|---|---|
| -eq | 等于则为真 |
| -ne | 不等于则为真 |
| -gt | 大于则为真 |
| -ge | 大于等于则为真 |
| -lt | 小于则为真 |
| -le | 小于等于则为真 |
字符串测试
| 参数 | 说明 |
|---|---|
| = | 等于则为真 |
| != | 不相等则为真 |
| -z 字符串 | 字符串的长度为零则为真 |
| -n 字符串 | 字符串的长度不为零则为真 |
文件测试
| 参数 | 说明 |
|---|---|
| -e 文件名 | 如果文件存在则为真 |
| -r 文件名 | 如果文件存在且可读则为真 |
| -w 文件名 | 如果文件存在且可写则为真 |
| -x 文件名 | 如果文件存在且可执行则为真 |
| -s 文件名 | 如果文件存在且至少有一个字符则为真 |
| -d 文件名 | 如果文件存在且为目录则为真 |
| -f 文件名 | 如果文件存在且为普通文件则为真 |
| -c 文件名 | 如果文件存在且为字符型特殊文件则为真 |
| -b 文件名 | 如果文件存在且为块特殊文件则为真 |
循环
循环结构允许我们执行重复的步骤或者在若干个不同条目上执行相同的程序。Bash中有下面两种循环
- for 循环
- while 循环
For 循环
直接举个例子
#!/bin/bash for X in red green blue do echo "$X" done
输出结果:

For循环会遍历空格分开的条目。注意,如果某一项含有空格,必须要用引号引起来
#!/bin/bash colour1="red" colour2="light blue" colour3="dark green" for X in "$colour1" $colour2" $colour3" do echo "$X" done
如果写成这样那么,根据空格分开输出结果为:

这个例子说明,除非你确认变量中不会包含空格,否则最好都用引号将变量保护起来。
在For循环中使用通配符
如果shell解析字符串时遇到*号,会将它展开为所有匹配的文件名。当且仅当目标文件与号展开后的字符串一致才会匹配成功。例如,单独的*号展开为当前目录的所有文件,中间以空格分开(包含隐藏文件)。
所以:
- 列出当前目录下的所有文件和目录。
echo *
- 列出所有的jpeg图片格式的文件。
echo *.jpg
- 列出home目录中public_html目录下的所有jpeg文件。
echo ${HOME}/public_html/*.jpg
正是由于这种特性,使得我们可以很方便的来操作目录和文件,尤其是和for循环结合使用时,更是便利。
#!/bin/bash for X in *.sh do grep -L 'if' "$X" done
打印出当前目录下所有不包含if字段的.sh文件。输出结果如下:

while循环
当给定条件为真值时,while循环会重复执行。例如:
#!/bin/bash X=0 while [ "$X" -lt 20 ] do echo "$X" X=$((X+1)) done
X=$((X+1)) 也可以写成 X=$(expr "$X" + 1),其输出结果如下:

这样导致这样的疑问: 为什么bash不能使用C风格的for循环呢?
for (X=1,X<10; X++)
这也跟bash自身的特性有关,之所以不允许这种for循环是由于:bash是一种解释性语言,因此其运行效率比较低。也正是由于这个原因,高负荷迭代是不允许的。
命令替换
Bash shell有个非常好用的特性叫做命令替换。允许我们将一个命令的输出当做另一个命令的输入。比如你想要将命令的输出赋值给变量X,你可以通过变量替换来实现。
有两种命令替换的方式:大括号扩展和反撇号扩展。
大括号扩展: $(commands) 会展开为命令commands的输出结果。并且允许嵌套使用,所以commands中允许包含子大括号扩展。
反撇好扩展:将commands扩展为命令commands的输出结果。不允许嵌套。
#/bin/bash file="$(ls)" web_file=`ls -al test.sh` echo "$file" echo "$web_file" X=`expr 3 * 2 + 1` echo "$X"
输出结果为:

$()替换方式的优点不言自明:非常易于嵌套。并且大多数bourne shell的衍生版本都支持(POSIX shell 或者更好的都支持)。
不过,反撇号替换更简单明了,即使是最基本的shell它也提供了支持(任意版本的#!/bin/sh都可以)。
参考:http://blog.jobbole.com/85183/
浙公网安备 33010602011771号