shell入门学习
作者:Seiya
时间:2019年05月17日
前言
起初,计算机从卡片或磁带中读入程序并生成单个报表。没有操作系统,也没有图形化显示器,甚至连交互式提示符都没有。
到了20世纪60年代,计算机开始支持使用交互式终端(通常是电传打字设备或高级打字机)来调用命令。
当贝尔实验室为全新的Unix操作系统创建了交互式用户界面之后,计算机便拥有了一项独有的特性。它可以从文本文件(称为shell脚本)中读取并执行命令,就好像这些命令是在终端中输入的一样。
这种能力是生产力上的一次巨大飞跃。程序员们再也不用输入一堆命令来执行一系列操作,shell脚本不仅节省了时间,而且清楚明白地表明了所执行的操作。
预备基础知识
终端提示符
命令都是在终端会话中输入并执行的。打开终端时会出现一个提示符。其形式通常如下:
username@hostname$
$表示普通用户,#表示管理员用户root。
提示:
root是Linux系统中权限最高的用户。
脚本的开头
shell脚本通常以 shebang
起始,如下所示:
#!/bin/bash
/bin/bash是Bash的解释器命令路径,脚本中只有第一行可以使用 shebang
来定义解释该脚本所使用的解释器。shebang是一个文本行,其中#!位于解释器路径之前。
shebang这个词其实是两个字符名称(sharp-bang)的简写。在Unix的行话里,用sharp或hash(有时候是mesh)来 称呼字符“ # ”, 用bang来称呼惊叹号“ ! ”, 因而shebang合起来就代表了这两个字符。
脚本的注释
注释部分以#为起始,一直延续到行尾。注释行通常用于描述代码或是在调试期间禁止执行某行代码。
脚本的运行
chmod u+x
赋予文件属主执行文件的权限;直接调用解释器, 将脚本文件作为参数传入 (比如
bash hi.sh
);
shell编程基础
shell脚本的关键在于输入多个命令并处理每个命令的结果,可以让你将多个命令串起来,一次执行完成,甚至可以将一个命令的结果传给另一个命令。
显示消息
大多数 shell
命令都会产生自己的输出,这些输出会显示在脚本所运行的控制台显示器上。但很多时候,你可能需要通过 echo
命令添加自己的文本消息来告诉脚本用户脚本正在做什么。
echo命令可用单引号或双引号来划定文本字符串:
echo "当前时间为: "
如果想把文本字符串和命令输出显示在同一行中,可以使用 echo -n
命令:
echo -n "当前时间为: "
使用变量
1. 环境变量
shell维护着一组环境变量,用来记录特定的系统信息。比如系统的名称、登录到系统上的用户名、用户的系统ID等。在脚本中,你可以在环境变量名称之前加上美元符($)来使用这些环境变量:
echo "当前用户为: $USER"
echo UID: $UID
echo HOME: $HOME
2. 用户变量
定义变量允许临时存储数据并在整个脚本中使用,从而使shell脚本看起来更像一个真正的计算机程序。
使用等号将值赋给用户变量。在变量、等号和值之间不能出现空格:
days=10
guest="攻城狮"
echo "$guest 在 $days 天前测试了一个脚本"
提示:
在涉及环境变量名时,什么时候该使用 $ ,什么时候不该使用 $ ,实在让人摸不着头脑。记住一点就行了:如果要用到变量,使用 $ ;如果要操作变量,不使用 $ 。这条规则的一个例外就是使用 printenv
显示某个变量的值。
命令替换
shell脚本中最有用的特性之一就是可以从命令输出中提取信息,并将其赋给变量。
将命令输出赋给变量的两种方法:
- 反引号字符(`)
#!/bin/bash
testing=`date`
echo "当前时间为: " $testing
# 输出
当前时间为: 2019年 5月17日 星期五 15时06分58秒 CST
- $( )格式
#!/bin/bash
testing=$(date)
echo "当前时间为: " $testing
# 输出
当前时间为: 2019年 5月17日 星期五 15时06分58秒 CST
警告一:
命令替换会创建一个子shell来运行对应的命令。子shell是由运行该脚本的shell 所创建出来的一个独立的子shell。正因如此,由该子shell所执行命令是无法使用脚本中所创建的变量的。
警告二:
被双引号括起来的变量会发生变量替换,单引号不会。
重定向输入和输出
有些时候你想要保存某个命令的输出而不仅仅只是让它显示在显示器上。bash shell提供了几个操作符,可以将命令的输出重定向到另一个位置(比如文件)。
1. 输出重定向
最基本的重定向将命令的输出发送到一个文件中,如果输出文件已经存在了,重定向操作符会用新的文件数据覆盖已有文件。bash shell用大于号(>)来完成这项功能:
#!/bin/bash
date > test
cat test
# 输出
2019年 5月17日 星期五 15时06分58秒 CST
如果想要将命令的输出追加到已有文件中,可以用双大于号(>>)来追加数据:
#!/bin/bash
date >> test
cat test
# 输出
2019年 5月17日 星期五 15时06分58秒 CST
2019年 5月17日 星期五 15时07分28秒 CST
2. 输入重定向
输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。 输入重定向符号是小于号(<):
#!/bin/bash
wc < test
# 输出
2 10 96
还有另外一种输入重定向的方法,称为内联输入重定向。这种方法无需使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据就可以了。
wc << EOF
> wo jiu shi chuan shuo
> wo jiao ao le ma
> EOF
# 输出
2 10 40
提示
次提示符会持续提示,以获取更多的输入数据,直到你输入了作为文本标记的那个字符串。
管道连接
重定向输入和输出虽然很方便,但仍然是一种比较繁琐的信息生成方式。我们用不着将命令输出重定向到文件中,可以将其直接重定向到另一个命令,这个过程叫管道连接。
date | wc
# 输出
1 6 48
我们还可以搭配使用重定向和管道来将输出保存到文件中:
date | wc > test
注意:
不要以为由管道串起的两个命令会依次执行。Linux系统实际上会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送给第二个命令。数据传输不会用到任何中间文件或缓冲区。
执行数学运算
在shell脚本中有两种途径来进行数学运算:
1. expr命令
expr命令允许在命令行 上处理数学表达式,但是特别笨拙:
expr 1 + 5
尽管 expr 看起来很好用,实际上却有相当大的局限。比如:无法识别有特殊含义的运算符,运算符连接的两个数字之间必须含有空格,最令人头痛的是它在脚本中使用过程中相当麻烦,所以不推荐使用。
2. 使用方括号
在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[ operation ])将数学表达式围起来:
$ var1=$[1 + 5]
$ echo $var1
# 输出
6
$ var2=$[$var1 * 2]
$ echo $var2
# 输出
12
警告:
在bash shell脚本中进行算术运算会有一个主要的限制,那就是只支持整数运算。
3. 浮点解决方案
最常见的方案是使用内建的bash计算器:bc
#!/bin/bash
var1=$(echo "scale=4; 3.44 / 5" | bc)
echo The answer is $var1
# 输出
The answer is .6880
浮点运算是由内建变量scale控制的。必须将这个值设置为你希望在计算结果中保留的小数位数,否则无法得到期望的结果。上面的例子运用了命令替换,在脚本中实现了浮点运算。
这个方法适用于较短的运算,但有时你会涉及更多的数字。如果需要进行大量运算,最好的办法是使用内联输入重定向,它允许你直接在命令行中重定向数据:
#!/bin/bash
var1=10.46
var2=43.67
var3=33.2
var4=71
var5=$(bc << EOF
scale = 4
a1 = ( $var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
)
echo The final answer for this mess is $var5
# 输出
The final answer for this mess is 2813.9882
退出脚本
1. 退出状态码
shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。Linux提供了一个专门的变量 $?
来保存上个已执行命令的退出状态码。对于需要进行检查的命令,必须在其运行完毕后立刻查看或使用$?变量。
按照惯例,一个成功结束的命令的退出状态码是0。如果一个命令结束时有错误,退出状态码就是一个正数值。
几个常见的退出状态码:
- 无效命令会返回一个退出状态码 127;
- 退出状态码 126 表明用户没有执行命令的正确权限;
- 退出状态码 1 表明给某个命令提供了无效参数;
2. exit命令
默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出。你可以改变这种默认行为,返回自己的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码。
#!/bin/bash
var1=10
var2=30
var3=$[$var1 + $var2]
echo The answer is $var3
exit 3
也可以在exit命令的参数中使用变量:
exit $var3
注意:
需要注意的是,退出状态码最大只能是255。如果最终指定的值超过255,shell会通过进行取余获取最终值。
shell逻辑控制
for循环
bash shell中for循环的基本语法:
for var in list
do
commands
done
c语言风格的语法:
for (( a = 1; a < 10; a++ ))
do
commands
done
while循环
bash shell中while循环的基本语法:
while test command
do
other commands
done
until循环
until命令和while命令工作的方式完全相反。until命令要求你指定一个通常返回非零退出状态码的测试命令。只有测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。
bash shell中until循环的基本语法:
until test command
do
other commands
done
嵌套循环
循环语句可以在循环内使用任意类型的命令,包括其他循环命令。
循环控制
1. break命令
break 命令是退出循环的一个简单方法。 可以用 break命令来退出任意类型的循环, 包括 while和until循环。
使用break命令的几种情况:
跳出单个循环
跳出内部循环
跳出外部循环
2. continue命令
continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。
条件控制
if命令
bash shell中if语句的基本语法:
if condition1
then
command1
elif condition2
then
command2
else
command3
fi
条件判断
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的判断:
- 数值判断:
- 字符串判断:
- 文件判断:
shell处理用户输入
命令行参数
向shell脚本传递数据的最基本方法是使用命令行参数。
$ ./test.sh 10 30
1. 读取参数
bash shell会将一些特殊变量分配给输入到命令行中的所有参数,这些参数称为位置参数(positional parameter)。比如:$0
是程序名,$1
是第一个参数,$2
是第二个参数,依次类推,直到第九个参数 $9
。
提示:
如果脚本需要的命令行参数不止9个,你仍然可以处理,但你必须在变量数字周围加上花括号,比如 ${10}
。
注意:
将文本字符串作为参数传递时,引号并非数据的一部分。它们只是表明数据的起止位置。
2. 参数统计
bash shell提供了一个特殊变量 $#
,它含有脚本运行时携带的命令行参数的个数,你可以在脚本中任何地方使用这个特殊变量。
3. 抓取参数
有时候需要抓取命令行上提供的所有参数,我们可以使用一组其他的特殊变量来解决这个问题。
$*
变量
$*
变量会将命令行上提供的所有参数当作一个单词保存。
$@
变量
$@
变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
处理选项
未完待续...