作者:jicanmeng
时间:2016年05月05日
两个命令可以用分号;
或逻辑与符号&&
隔开,写在一行,在运行时按顺序运行。区别在于符号;
前后的两条命令都会执行,而符号&&
前面的命令执行成功,后面的命令才会执行。(P200)
[jicanmeng@andy tmp]$ ls
rpm.txt
[jicanmeng@andy tmp]$ date
Thu May 5 22:36:59 CST 2016
[jicanmeng@andy tmp]$ ls; date
rpm.txt
Thu May 5 22:37:03 CST 2016
[jicanmeng@andy tmp]$ lsaaa; date
bash: lsaaa: command not found
Thu May 5 22:37:07 CST 2016
[jicanmeng@andy tmp]$ lsaaa && date
bash: lsaaa: command not found
[jicanmeng@andy tmp]$
创建shell脚本时,必须在文件的第一行指定要使用的shell。其格式为:#!/bin/bash
。(P201)
符号#表示注释。但是第一行是个特例,它不表示注释。(P201)
bash脚本的第一行指定了默认情况下使用哪个shell来执行本脚本。假设有两个脚本a.sh和b.sh,内容如下:
[jicanmeng@andy tmp]$ cat a.sh
#!/bin/bash
sleep 20
[jicanmeng@andy tmp]$ cat b.sh
#!/bin/sh
sleep 20
[jicanmeng@andy tmp]$
我们打开三个shell,分别对应pts/1,pts/2,pts/3。在pts/1中执行命令: ./a.sh
,在pts/2中执行命令: ./b.sh
,在pts/3中执行命令: ps -ef
。命令输出如下:
[jicanmeng@andy tmp]$ ps -ef
...
500 2870 1 0 May03 ? 00:00:13 /usr/bin/gnome-terminal -x /bin/
...
500 5870 2870 0 22:29 pts/1 00:00:00 /bin/bash
500 5885 2870 0 22:29 pts/2 00:00:00 /bin/bash
500 5900 2870 0 22:29 pts/3 00:00:00 /bin/bash
500 5913 5870 0 22:29 pts/1 00:00:00 /bin/bash ./a.sh
500 5914 5913 0 22:29 pts/1 00:00:00 sleep 20
500 5915 5885 0 22:29 pts/2 00:00:00 /bin/sh ./b.sh
500 5916 5915 0 22:29 pts/2 00:00:00 sleep 20
500 5917 5900 0 22:29 pts/3 00:00:00 ps -ef
[jicanmeng@andy tmp]$
可以看出,a.sh脚本是被bash执行的,b.sh是被sh执行的,这都是在脚本第一行指定的。但是如果在pts/1中执行命令: bash a.sh
,在pts/2中执行命令: bash b.sh
,在pts/3中执行命令: ps -ef
,即显式指定bash来读取脚本并执行命令,那么在ps -ef
的输出可以看到a.sh和b.sh都是被bash执行的。
echo命令使用 -n 参数时,不会换行。(P203)
[jicanmeng@andy tmp]$ cat a.sh
#!/bin/bash
echo "Today is: "
date
[jicanmeng@andy tmp]$ cat b.sh
#!/bin/bash
echo -n "Today is: "
date
[jicanmeng@andy tmp]$ ./a.sh
Today is:
Thu May 5 23:00:38 CST 2016
[jicanmeng@andy tmp]$ ./b.sh
Today is: Thu May 5 23:00:40 CST 2016
[jicanmeng@andy tmp]$
$PATH
。
${PATH}
。
variable=value
。[jicanmeng@andy tmp]$ var="lang is $LANG"
[jicanmeng@andy tmp]$ echo $var
lang is en_US.UTF-8
[jicanmeng@andy tmp]$ var='lang is $LANG'
[jicanmeng@andy tmp]$ echo $var
lang is $LANG
[jicanmeng@andy tmp]$
$
, \
, 空白字元, '
, "
等)变成一般字元。例如:
[jicanmeng@andy tmp]$ name=canmeng\"\$
[jicanmeng@andy tmp]$ echo $name
canmeng"$
[jicanmeng@andy tmp]$ name=can\ meng
[jicanmeng@andy tmp]$ echo $var
can meng
[jicanmeng@andy tmp]$
"$变数名称"
或 ${变数}
累加内容。如下所示:
[jicanmeng@andy tmp]$ name=canmeng
[jicanmeng@andy tmp]$ name="$name"One
[jicanmeng@andy tmp]$ echo $name
canmengOne
[jicanmeng@andy tmp]$ name=${name}Two
[jicanmeng@andy tmp]$ echo $name
canmengOneTwo
[jicanmeng@andy tmp]$
在不引起混淆的情况下,也可以直接用 $变数内容
来累加内容。例如:
[jicanmeng@andy tmp]$ name=canmeng
[jicanmeng@andy tmp]$ name=$nameOne
[jicanmeng@andy tmp]$ echo $name
[jicanmeng@andy tmp]$ name=$name:Two
[jicanmeng@andy tmp]$ echo $name
:Two
[jicanmeng@andy tmp]$ name=$name" Three"
[jicanmeng@andy tmp]$ echo $name
:Two Three
[jicanmeng@andy tmp]$
可以看到,第一个例子引起了混淆,因为根本就没有为名称为nameOne的变量,所以name变量值为空。第二个例子和第三个例子都没有引起混淆,因为变量名的组成字符只能是字母、数字和下划线。不过,最好我们还是用标准的方法,使用 "$变数名称"
或 ${变数}
的形式。
[jicanmeng@andy tmp]$ version=$(uname -r)
[jicanmeng@andy tmp]$ echo $version
2.6.32-431.el6.x86_64
[jicanmeng@andy tmp]$ version=`uname -r`
[jicanmeng@andy tmp]$ echo $version
2.6.32-431.el6.x86_64
[jicanmeng@andy tmp]$ cd /lib/modules/`uname -r`/kernel
[jicanmeng@andy tmp]$ pwd
/lib/modules/2.6.32-431.el6.x86_64/kernel
[jicanmeng@andy tmp]$
unset 变量名称
。使用符号>进行输出重定向。使用符号<进行输入重定向。(P208)
使用符号>>进行输出重定向,和>差异如下:>直接覆盖重定向的文件的原有内容,而>>则在重定向的文件的尾部追加数据。
还有另外一种输入重定向的方法,称为内联输入重定向(inline input redirection)。这种方法允许你在命令行而不是从文件指定输入被重定向的数据。内联输入重定向的符号是<<。除了这个符号,你必须指定一个文本标记来划分要输入数据的开始和结尾。你可以用任何字符串的值来作为文本标记,但在数据的开始和结尾必须一致。例如:
[jicanmeng@andy tmp]$ cat > version-check.sh << "EOF"
> gcc -v
> gdb -v
> EOF
[jicanmeng@andy tmp]$ ls
version-check.sh
[jicanmeng@andy tmp]$ cat version-check.sh
gcc -v
gdb -v
[jicanmeng@andy tmp]$ echo $PS2
>
[jicanmeng@andy tmp]$
在命令行上使用内联输入重定向时,shell会用PS2环境变量中定义的次提示符来提示输入数据。由上面最后一条命令也可以看出来,次提示符是>。
准确的说,<是对文件描述符STDIN重定向,>是对文件描述符STDOUT重定向。
管道的符号是|,放在命令之间,用于将一个命令的输出重定向到另一个命令的输入上。(P210)
不要以为管道链接会一个一个地运行。Linux系统实际上会同时运行这两个命令,在系统内部将它们连接起来???(第一个命令还没有运行完,第二个命令即使运行也没有输入呀?)
shell中有两种途径来进行数学运算操作。
[jicanmeng@andy tmp]$ var1=`expr 5 * 2`
expr: syntax error
[jicanmeng@andy tmp]$ var1=`expr 5 \* 2`
[jicanmeng@andy tmp]$ echo $var1
10
[jicanmeng@andy tmp]$
在实际使用中,这种方法比较少见,我们要重点关注第二种方法。
[jicanmeng@andy tmp]$ var1=$[5*2]
[jicanmeng@andy tmp]$ echo $var1
10
[jicanmeng@andy tmp]$ var1=$[ 5 * 3 ]
[jicanmeng@andy tmp]$ echo $var1
15
[jicanmeng@andy tmp]$
注意,在使用方括号时,不用担心shell会误解乘号*或其它符号。shell知道它不是通配符,因为它在方括号内。
bash shell数学运算符只支持整数运算。如果要在bash shell脚本中进行浮点数运算,我们可以使用bash计算器,称作bc。用法如下:
例1:
[jicanmeng@andy tmp]$ echo "3*4" | bc
12
例2:
[jicanmeng@andy tmp]$ echo "scale=3;12.44/5" | bc
2.488
[jicanmeng@andy tmp]$
例3:
[jicanmeng@andy tmp]$ bc -q
12.44/5
2
scale=4
12.44/5
2.4880
var1=10
var1 * 3
30
var2 = 20
var3 = var2 * 5
print var3
100
quit
[jicanmeng@andy tmp]$
几个地方需要注意:1. bc可以工作在交互模式,也可以工作在非交互模式; 2. 和bash shell不同,bc中变量的赋值表达式中,等号前后是可以有空格的; 3. 和bash shell不同,bc中的变量在引用的时候,是不需要加$符号的。4. scale内建变量表示浮点数的精度; 5. -q选项表示将bash计算器输出的长长的欢迎信息屏蔽掉。
例4:
[jicanmeng@andy tmp]$ var=`echo "scale = 4; 12.44 / 5" | bc`
[jicanmeng@andy tmp]$ echo $var
2.4880
[jicanmeng@andy tmp]$
例5:
[jicanmeng@andy tmp]$ cat test10
#!/bin/bash
var1=100
var2=45
var3=`echo "scale = 4; $var1 / $var2" | bc`
echo The answer is $var3
[jicanmeng@andy tmp]$ bash test10
The answer is 2.2222
[jicanmeng@andy tmp]$
例6:
[jicanmeng@andy tmp]$ cat test11
#!/bin/bash
var3=`echo "var1 = 100; var2 = 45; scale = 4; var1 / var2" | bc`
echo The answer is $var3
[jicanmeng@andy tmp]$ bash test11
The answer is 2.2222
[jicanmeng@andy tmp]$
需要注意的是:在上面的例3中,引用变量时前面不需要加$符号,而在例5中,给var3变量赋值语句中,引用var2和var1变量时都用了$符号,因为这两个变量都不是bc定义的变量,而是bash定义的变量。从例5和例6相比较可以看出差异。
例7:
[jicanmeng@andy tmp]$ cat test12
#!/bin/bash
var1=100
var3=`bc << EOF
var2 = 45
scale = 4
$var1 / var2
EOF`
echo The answer if $var3
[jicanmeng@andy tmp]$ bash test12
The answer is 2.2222
[jicanmeng@andy tmp]$
shell中运行的每个命令都使用退出状态码(exit status)来告诉shell它完成了处理。可以通过 echo $? 命令来查看上个执行的命令的退出状态码。《鸟哥的linux私房菜》中有一个非常好的例子:
[jicanmeng@andy tmp]$ 12name=VBird
bash: 12name=VBird: command not found
[jicanmeng@andy tmp]$ echo $?
127
[jicanmeng@andy tmp]$ echo $?
0
[jicanmeng@andy tmp]$
默认情况下,shell脚本的退出状态码就是脚本中最后一个命令的退出状态码。不过,我们可以通过exit命令在脚本结束运行时指定一个退出状态码。例如:
例1:
[jicanmeng@andy tmp]$ cat test13
echo "hello,world"
exit 5
[jicanmeng@andy tmp]$ bash test13
hello,world
[jicanmeng@andy tmp]$ echo $?
5
[jicanmeng@andy tmp]$
例2:
[jicanmeng@andy tmp]$ cat test14
echo "hello,world"
var=3
exit $var
[jicanmeng@andy tmp]$ bash test14
hello,world
[jicanmeng@andy tmp]$ echo $?
3
[jicanmeng@andy tmp]$
需要注意的是,退出状态码最大只能是255。
在bash shell中下达的命令,会启动一个新的shell,在这个新的shell中执行命令,执行完毕后会回到原来的shell。当下达的命令是shell的 built-in指令时,才不会新创建一个子shell。子shell会继承父shell的环境变量(environment variables)。我们可以在父shell中执行export variableName
来将一个变量变为环境变量,这样在子shell中也可以使用这个变量的值了。
name=canmeng ls
这条命令为什么可以执行??? 先定义一个变量,再执行一条命令,书里面并没有提到这种格式啊???
cat a.txt
和cat < a.txt
两条命令都可以显示a.txt文件的内容,那么这两条命令有什么区别呢? 答:如果cat命令没有输入参数,那么它会从标准输入STDIN读取数据,如果有,就从输入参数对应的文件读取数据。所以区别是cat < a.txt
命令从标准输入读取的数据,但标准输入被重定向到了a.txt文件中。
例1:
[jicanmeng@andy tmp]$ var6=`bc << "EOF"
> var1 = 100
> var2 = 45
> scale = 3
> var1 / var2
> EOF`
[jicanmeng@andy tmp]$ echo $var6
2.222
[jicanmeng@andy tmp]$
于是问题来了,为什么在命令上直接输入没有问题,而写到脚本中就出错呢?