第三十二章: 位置参数
32.1 访问命令行
shell提供了一组称为位置参数的变量, 用于存储命令行中的关键字, 这些变量分别命名为0-9, 使用方法如下:
#!/bin/bash
# posit-param: script to view command line parameters
echo "
\$0 = $0
\$1 = $1
\$2 = $2
\$3 = $3
\$4 = $4
\$5 = $5
\$6 = $6
\$7 = $7
\$8 = $8
\$9 = $9
"
即便没有提供任何实参, 变量 $0 总是会存储命令行显示的第一行数据, 也就是所执行程序的路径名.
使用参数扩展技术, 可以获取多余9个的参数, 为标明一个大于9的数字, 将数字用大括号括起来即可, 例如 ${211} .
32.1.1 确定实参的数目
shell还提供了变量 $# 以给出命令行参数的数目. 例如:
#!/bin/bash
# posit-param: script to view command line parameters
echo "
Number of arguments: $#
\$0 = $0
\$1 = $1
\$2 = $2
\$3 = $3
\$4 = $4
\$5 = $5
\$6 = $6
\$7 = $7
\$8 = $8
\$9 = $9
"
32.1.2 shift-处理大量的实参
为了处理大量的实参, shell提供了shift命令, 每次执行shift命令之后所有的参数均下移一位, 这样就可以只处理一个参数($0) 而完成全部的任务.
#!/bin/bash
# posit-param2: script to display all arguments
count=1
while [[ $# -gt 0 ]]; do
echo "Argument $count = $1"
count=$((count + 1))
shift
done
32.1.3 简单的应用程序
一个简单的使用位置参数的程序如下:
#!/bin/bash
# file_info: simple file information program
PROGNAME=$(basename $0)
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
echo -e "\nFile Status:"
stat $1
else
echo "$PROGNAME: usage: $PROGNAME file" >&2
exit 1
fi
这个程序输出了单个特定文件的文件类型和文件状态, 并且使用basename命令移除路径名的起始部分, 只留下基本的文件名.
32.1.4 在shell函数中使用位置参数
位置参数也可用于shell函数实参的传递, 例子如下:
file_info () {
# file_info: function to display file information
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
echo -e "\nFile Status:"
stat $1
else
echo "$FUNCNAME: usage: $FUNCNAME file" >&2
return 1
fi
}
shell会自动更新FUNCNAME变量以追踪当前执行的shell函数, 但是变量$0包含的总是命令行第一项的路径全名.
32.2 处理多个位置参数
shell提供了两种特殊的参数, 它们都能扩展为一个完整的位置参数列, 如下表:
参数 | 描述 |
---|---|
$* | 扩展为从1开始的位置参数列, 当包括在双引号内时, 扩展为双引号引用的由全部位置参数构成的字符串, 位置参数由IFS变量的第一个字符间隔开 |
$@ | 扩展为从1开始的位置参数列, 当包括在双引号内时, 将每个位置参数扩展为双引号引用的单独单词 |
一个演示程序如下:
#!/bin/bash
# posit-params3 : script to demonstrate $* and $@
print_params () {
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
}
pass_params () {
echo -e "\n" '$* :'; print_params $*
echo -e "\n" '"$*" :'; print_params "$*"
echo -e "\n" '$@ :'; print_params $@
echo -e "\n" '"$@" :'; print_params "$@"
}
pass_params "word" "words with spaces"
输出如下:
[me@linuxbox ~]$ posit-param3
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
$@ :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$@" :
$1 = word
$2 = words with spaces
$3 =
$4 =
$@保持了每个位置参数的完整性, 是大多数情况下最令人满意的方法.
32.3 更完整的应用程序
现在回到 sys_info_page 程序, 添加如下所示的命令行选项:
- 输出文件: 指定输出文件的选项, 形式为 -f file 或者 --file file
- 交互模式: 这个选项会提示用户输出文件的名称, 并校验此文件是否已经存在. 若存在则覆盖之前提示用户. 形式为 -i 或者 --interactive
- 帮助: 输出帮助性质的使用说明, 形式为 -h或者 --help
以下是完善命令行处理功能所需的代码:
usage () {
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
return
}
# process command line options
interactive=
filename=
while [[ -n $1 ]]; do
case $1 in
-f | --file) shift
filename=$1
;;
-i | --interactive) interactive=1
;;
-h | --help) usage
exit
;;
*) usage >&2
exit 1
;;
esac
shift
done
以下是完善交互模式的代码:
# interactive mode
if [[ -n $interactive ]]; then
while true; do
read -p "Enter name of output file: " filename
if [[ -e $filename ]]; then
read -p "'$filename' exists. Overwrite? [y/n/q] > "
case $REPLY in
Y|y) break
;;
Q|q) echo "Program terminated."
exit
;;
*) continue
;;
esac
elif [[ -z $filename ]]; then
continue
else
break
fi
done
fi
将写页面的代码改写为shell函数:
write_html_page () {
cat <<- _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIMESTAMP</P>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</BODY>
</HTML>
_EOF_
return
}
# output html page
if [[ -n $filename ]]; then
if touch $filename && [[ -f $filename ]]; then
write_html_page > $filename
else
echo "$PROGNAME: Cannot write file '$filename'" >&2
exit 1
fi
else
write_html_page
fi
32.4 本章结尾语
在位置参数的帮助下, 用户可以写出功能性更强的脚本. 对于重复性的任务, 位置参数使得用户可以写出很有帮助的shell函数, 并放置在用户的 .bashrc 文件中.
下面是完整的 sys_info_page 程序:
#!/bin/bash
# sys_info_page: program to output a system information page
PROGNAME=$(basename $0)
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"
report_uptime () {
cat <<- _EOF_
<H2>System Uptime</H2>
<PRE>$(uptime)</PRE>
_EOF_
return
}
report_disk_space () {
cat <<- _EOF_
<H2>Disk Space Utilization</H2>
<PRE>$(df -h)</PRE>
_EOF_
return
}
report_home_space () {
if [[ $(id -u) -eq 0 ]]; then
cat <<- _EOF_
<H2>Home Space Utilization (All Users)</H2>
<PRE>$(du -sh /home/*)</PRE>
_EOF_
else
cat <<- _EOF_
<H2>Home Space Utilization ($USER)</H2>
<PRE>$(du -sh $HOME)</PRE>
_EOF_
fi
return
}
usage () {
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
return
}
write_html_page () {
cat <<- _EOF_
<HTML>
<HEAD>
<TITLE>$TITLE</TITLE>
</HEAD>
<BODY>
<H1>$TITLE</H1>
<P>$TIMESTAMP</P>
$(report_uptime)
$(report_disk_space)
$(report_home_space)
</BODY>
</HTML>
_EOF_
return
}
# process command line options
interactive=
filename=
while [[ -n $1 ]]; do
case $1 in
-f | --file) shift
filename=$1
;;
-i | --interactive) interactive=1
;;
-h | --help) usage
exit
;;
*) usage >&2
exit 1
;;
esac
shift
done
# interactive mode
if [[ -n $interactive ]]; then
while true; do
read -p "Enter name of output file: " filename
if [[ -e $filename ]]; then
read -p "'$filename' exists. Overwrite? [y/n/q] > "
case $REPLY in
Y|y) break
;;
Q|q) echo "Program terminated."
exit
;;
*) continue
;;
esac
fi
done
fi
# output html page
if [[ -n $filename ]]; then
if touch $filename && [[ -f $filename ]]; then
write_html_page > $filename
else
echo "$PROGNAME: Cannot write file '$filename'" >&2
exit 1
fi
else
write_html_page
fi
- Bash Hackers Wiki 上有一篇不错的关于位置参数的文章:
http://wiki.bash-hackers.org/scripting/posparams
- Bash 的参考手册有一篇关于特殊参数的文章, 包括 $* 和 $@:
http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters
- 除了本章讨论的技术之外, bash 还包含一个叫做 getopts 的内部命令, 此命令也可以用来处理命令行参数. bash 参考页面的 SHELL BUILTIN COMMANDS 一节介绍了这个命令, Bash Hackers Wiki 上也有对它的描述: