基础shell小技巧003

1. 传递参数

1.1 读取参数

bash shell会将所有的命令行参数都指派给称作位置参数(positional parameter)的特殊变量。这也包括shell脚本名称。位置变量的名称都是标准数字:$0对应脚本名,$1对应第一个命令行参数,$2对应第二个命令行参数,以此类推,直到$9。

demo:

#!/bin/bash

product=$[ $1 * $2 ]
echo The first parameter is $1.
echo The second parameter is $2.
echo The product value is $product.
exit

结果:
PS D:\local_project\shell\14-处理用户输入\传递参数> sh test01.sh 2 5 
The first parameter is 2.
The second parameter is 5.
The product value is 10.

如果脚本需要的命令行参数不止9个,则仍可以继续加入更多的参数,但是需要稍微修改一下位置变量名。在第9个位置变量之后,必须在变量名两侧加上花括号,比如${10}。来看一个例子:

#!/bin/bash
# Handling lots of command-line parameters
#
product=$[ ${10} * ${11} ]
echo The tenth parameter is ${10}.
echo The eleventh parameter is ${11}.
echo The product value is $product.
exit

$ ./positional10.sh 1 2 3 4 5 6 7 8 9 10 11 12
The tenth parameter is 10.
The eleventh parameter is 11.
The product value is 110.

2.1 读取脚本名

可以使用位置变量$0获取在命令行中运行的shell脚本名。

编写一个脚本,生成能标识运行时间的日志消息:

# !/bin/bash

scriptname=$(basename $0)
echo The $scriptname ran at $(date) >> ./scripttrack.log

2. 特殊参数变量

2.1 参数统计

特殊变量$#含有脚本运行时携带的命令行参数的个数。你可以在脚本中的任何地方使用这个特殊变量,就跟普通变量一样:

# !/bin/bash

if [ $# -eq 1 ]
then
     fragment="parameter was"
else
     fragment="parameters were"
fi
echo $# $fragment supplied.

结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test01.sh 1
1 parameter was supplied.
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test01.sh 1 11 1 11 1 1 
6 parameters were supplied.

想获取用户传入的最后一个参数,得这么写:

# !/bin/bash

echo The number of parameters is $#
echo The last parameter is ${!#}

结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test02.sh 1 2 6
The number of parameters is 3
The last parameter is 6

2.2 获取所有数据

有时候你想要抓取命令行中的所有参数。这时无须先用$#变量判断有多少个命令行参数,然后再进行遍历,用两个特殊变量即可解决这个问题。

∗ 变量和 *变量和 变量和@变量可以轻松访问所有参数,它们各自包含了所有的命令行参数。*

* ∗ 变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上, *变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上, 变量会将所有的命令行参数视为一个单词。这个单词含有命令行中出现的每一个参数。基本上,*变量会将这些参数视为一个整体,而不是一系列个体。另外,$@变量会将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数。这通常使用for命令完成。

这两个变量的工作方式不太容易理解。下面来看一个例子,你就能明白二者之间的区别了:

# !/bin/bash
echo "Using the \$* method: $*"
count=1
for param in "$*"
do
     echo "\$* Parameter #$count = $param"
     count=$[ $count + 1 ]
done
echo "Using the \$@ method: $@"
count=1
for param in "$@"
do
     echo "\$@ Parameter #$count = $param"
     count=$[ $count + 1 ]
done

结果:
PS D:\local_project\shell\14-处理用户输入\特殊参数变量> sh .\test03.sh 1 2 3 4
Using the $* method: 1 2 3 4
$* Parameter #1 = 1 2 3 4
Using the $@ method: 1 2 3 4
$@ Parameter #1 = 1
$@ Parameter #2 = 2
$@ Parameter #3 = 3
$@ Parameter #4 = 4

3. 移动参数

bash shell工具箱中的另一件工具是shift命令,该命令可用于操作命令行参数。跟字面上的意思一样,shift命令会根据命令行参数的相对位置进行移动。

在使用shift命令时,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量$3的值会移入$2,变量$2的值会移入$1,而变量$1的值则会被删除(注意,变量$0的值,也就是脚本名,不会改变)。

这是遍历命令行参数的另一种好方法,尤其是在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量。

demo:

# !/bin/bash
echo "Using the shift method:"
count=1
while [ -n "$1" ]
do
     echo "Parameter #$count = $1"
     count=$[ $count + 1 ]
     shift
done

接下来看复杂一点的使用案例,处理用户输入选项:

# !/bin/bash

while [ -n "$1" ]
do
     case "$1" in
          -a) echo "Found the -a option" ;;
          -b) echo "Found the -b option" ;;
          -c) echo "Found the -c option" ;;
          --) shift
              break;;
          *) echo "$1 is not an option" ;;
     esac
     shift
done

count=1
for param in $@
do
     echo "Parameter #$count: $param"
     count=$[ $count + 1 ]
done

结果:
PS D:\local_project\shell\14-处理用户输入\处理选项> sh .\test01.sh -a -b -- 1 2
Found the -a option
Found the -b option
Parameter #1: 1
Parameter #2: 2

4. 处理选项

4.1 查找选项

# !/bin/bash

while [ -n "$1" ]
do
     case "$1" in
          -a) echo "Found the -a option" ;;
          -b) echo "Found the -b option" ;;
          -c) echo "Found the -c option" ;;
          --) shift
              break;;
          *) echo "$1 is not an option" ;;
     esac
     shift
done

count=1
for param in $@
do
     echo "Parameter #$count: $param"
     count=$[ $count + 1 ]
done

4.2 使用getopt命令

getopt命令可以接受一系列任意形式的命令行选项和参数,并自动将其转换成适当的格式。getopt的命令格式如下:

getopt optstring parameters

optstring是这个过程的关键所在。它定义了有效的命令行选项字母,还定义了哪些选项字母需要参数值。

首先,在optstring中列出要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后面加一个冒号。getopt命令会基于你定义的optstring解析提供的参数。

下面这个简单的例子演示了getopt是如何工作的:

$ getopt ab:cd -a -b BValue -cd test1 test2
 -a -b BValue -c -d -- test1 test2

optstring定义了4个有效选项字母:a、b、c和d。冒号(:)被放在了字母b后面,因为b选项需要一个参数值。当getopt命令运行时,会检查参数列表(-a -b BValue -cd test1 test2),并基于提供的optstring进行解析。注意,它会自动将-cd分成两个单独的选项,并插入双连字符来分隔命令行中额外的参数。

5. 获取用户输入

5.1 基本的读取

read命令从标准输入(键盘)或另一个文件描述符中接受输入。获取输入后,read命令会将数据存入变量。下面是该命令最简单的用法:

# !/bin/bash

echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my script."

实际上,read命令也提供了-p选项,允许直接指定提示符:

# !/bin/bash

read -p "Please enter your age: " age
days=$[ $age * 365 ]
echo "That means you are over $days days old!"

也可以在read命令中不指定任何变量,这样read命令便会将接收到的所有数据都放进特殊环境变量REPLY中:

# !/bin/bash

read -p "Enter your name: "
echo
echo "Hello $REPLY, welcome to my script."

使用read命令时要当心。脚本可能会一直苦等着用户输入。如果不管是否有数据输入,脚本都必须继续执行,你可以用-t选项来指定一个计时器。-t选项会指定read命令等待输入的秒数。如果计时器超时,则read命令会返回非0退出状态码:

# !/bin/bash

if read -t 5 -p "Enter your name: " name
then
     echo "Hello $name, welcome to my script."
else
     echo
     echo "Sorry, no longer waiting for name."
fi

你也可以不对输入过程计时,而是让read命令统计输入的字符数。当字符数达到预设值时,就自动退出,将已输入的数据赋给变量:

#!/bin/bash

read -n 1 -p "Do you want to continue [Y/N]? " answer
#
case $answer in
Y | y) echo
       echo "Okay. Continue on...";;
N | n) echo
       echo "Okay. Goodbye"
       exit;;
esac

我们也可以使用read命令读取文件。每次调用read命令都会从指定文件中读取一行文本。当文件中没有内容可读时,read命令会退出并返回非0退出状态码。

其中麻烦的地方是将文件数据传给read命令。最常见的方法是对文件使用cat命令,将结果通过管道直接传给含有read命令的while命令。来看下面的例子:

# !/bin/bash

count=1
cat line.txt | while read line
do
     echo "Line $count: $line"
     count=$[ $count + 1 ]
done
echo "Finished processing the file."

你可能感兴趣的:(Shell,服务器,linux,运维)