#3: 传参给脚本
到目前为止,已经学习了怎么创建脚本和使用变量,在脚本中使用变量可以让脚本更灵活的响应各种不同的数据和用户输入。
向bash脚本里面传参
下面的这个脚本count_lines.sh,将根据用户输入的文件统计出该文件中有多少行:
#!/bin/bash
echo -n "Please enter a filename: "
read filename
nlines=$(wc -l < $filename)
echo "There are $nlines lines in $filename"
截图中,用户输入文件/etc/passwd,脚本统计出了这个文件中的行数,并且将结果输出打印在命令行中。

这个脚本已经可以很好的运行了,但是还有另外一个更好的选择!
可以在用户运行脚本的时候同时输入文件名作为运行参数传递给脚本,而不是提示用户输入文件名。如下所示:
./count_lines.sh /etc/passwd
bash脚本的第一个参数(也称为位置参数)可以通过$1来获取访问到。
所以,在count_lines.sh脚本中,使用$1来替代filename变量。如下所示:
#!/bin/bash
nlines=$(wc -l < $1)
echo "There are $nlines lines in $1"
相信大家都注意到了,修改后的脚本,去掉第一个的echo提示和后面的read获取输入这两行代码。使得脚本简化到只有两行。
最后,你能在运行这个脚本的时候像其他命令增加参数一样,传任何文件名作为参数给脚本。
./count_lines.sh /etc/group
There are 73 lines in /etc/group

传多个参数给bash脚本
通常,你也可以向bash脚本传递多个参数。以下是传递多个参数的语法:
script.sh arg1 arg2 arg3 …
第二个参数由$2变量引用,第三个参数由$3引用,以此类推。
如果你想知道或者使用脚本的名称,可以引用$0。
现在修改 count_lines.sh 脚本,让它可以计算多个文件的行数:
#!/bin/bash
n1=$(wc -l < $1)
n2=$(wc -l < $2)
n3=$(wc -l < $3)
echo "There are $n1 lines in $1"
echo "There are $n2 lines in $2"
echo "There are $n3 lines in $3"
运行脚本并同时输入多个文件,看看执行的结果:

如你所见,脚本输出三个文件中每个文件的行数,强调一下,参数的顺序很重要。
我们可以编辑count_lines.sh脚本,以便它可以计算多个文件的行数。
有创造性的使用bash参数
在众多的Linux命令中有一些命令比较复杂,它们可能有很长的语法或者一大串的参数选项供你使用。
幸运的是,你可以使用bash参数将一个困难的命令变成一个相对简单的任务
为了演示,请看下面的find.sh脚本:
#!/bin/bash
find / -iname $1 2> /dev/null
这是一个非常简单的脚本,但是可以证明非常有用。你可以为脚本提供任何文件名作为参数,它可以显示文件的位置:

你可以看到,这比输入整个find命令又长又复杂一大串的命令要简单得多。
2> /dev/null是什么意思,你应该很感兴趣吧,其实这是Linux的输入、输出和错误重定向的内容,2表示任何错误信息,把它重定向到/dev/null空设备中去,也就是所,抛弃错误信息,这些错误信息不会在屏幕命令行中被显示出来。
重定向是Linux中一个基本概念。学习如何在Linux命令行中使用标准输入、标准输出、标准错误和管道重定向是初学者必须掌握的概念之一。
额外提示: Bash shell中的特殊变量
bash有很多内置的特殊变量,在编写脚本的时候方便随时使用
下表中列出了几个最常见常用的特殊内置变量
| 特殊变量 | 解释 |
|---|---|
| $0 | bash脚本的文件名 |
| $1, $2...$n | bash脚本的传入参数 |
| $$ | 当前shell的进程ID |
| $# | 传入bash脚本的参数个数总数 |
| $@ | 传入bash脚本的参数全部内容 |
| $? | 最后执行的命令的退出状态 |
| $! | 最后执行的命令的进程ID |
下面的variables.sh脚本演示了这些特殊变量的作用:
#!/bin/bash
echo "Name of the script: $0"
echo "Total number of arguments: $#"
echo "Values of all the arguments: $@"
可以传递任何你想要传进去的参数并运行脚本:

到处,本节的内容已经结束,本节介绍了bash参数的传递和调用。
练习题
自己动手做练习可以帮助你记忆刚学到的东西,下面是几到简单的练习题,用到了上面提到的内容。如果您是初学者,建议您可以自己动手做一下。
练习 1: 这个脚本叫什么名字?
写一个shell脚本打印出这个脚本的文件名。
难度级别:容易
提示:可以使用$0命令来获取文件名。
练习 2: 让我反过来看
写一个shell脚本,传入三个参数并将他们反过来打印输出。
难度级别:容易
提示:因为在这个阶段没有涉及if-else,所以不需要检查参数的数量。只需确保为脚本提供3个参数。
输出结果的例子:
./reverse.sh Ritchie Stallman Torvalds
Output:
Original Input - Ritchie Stallman Torvald
Reversed Output - Torvalds Stallman Ritchie
练习 3: 创建一个目录和一个文件?
编写一个shell脚本,如果hello目录不存在,创建一个名为hello的目录名。切换到该目录并创建一个名为new.txt的新文件。
难度级别:容易
提示:用mkdir命令加-p参数。
练习 4: 统计行数和单词数?
创建一个名为sample.txt的文件,并且添加下面的内容
This is a sample file.
A sample file contains sample text.
It can be easily counted using wc command.
将这个文件作为参数传给脚本,脚本统计和输出这个文件的行数和单词数
难度级别:中等
提示:使用wc命令统计文件的行数和单词数,使用cut命令从wc命令输出中删除文件名。
练习 5: 这个命令在哪里
写一个脚本,输入一个命令作为参数输出该命令的二进制文件路径。
难度级别:容易
提示:提前使用使用whereis命令或者which命令。
练习题答案
答案 1:这个脚本叫什么名字?
写一个shell脚本打印出这个脚本的文件名。
#!/bin/bash
echo "This script is called: $0"
答案 2:让我反过来看
写一个shell脚本,传入三个参数并将他们反过来打印输出。
#!/bin/bash
echo "Original Input - $1 $2 $3"
echo "Reversed Output - $3 $2 $1"
答案 3:创建一个目录和一个文件?
编写一个shell脚本,如果hello目录不存在,创建一个名为hello的目录名。切换到该目录并创建一个名为new.txt的新文件。
#!/bin/bash
mkdir -p $1
cd $1
touch new.txt
答案 4:统计行数和单词数?
传sample.txt文件给脚本统计和输出这个文件的行数和单词数
#!/bin/bash
lines=$(wc -l $1 | cut -d ' ' -f 1)
words=$(wc -w $1 | cut -d ' ' -f 1)
echo "Number of lines in $1 are $lines"
echo "Number of words in $1 are $words"
答案 5:这个命令在哪里
写一个脚本,输入一个命令作为参数输出该命令的二进制文件路径。
#!/bin/bash
location=$(which $1)
echo "Location of the binary of the command $1 is $location"