ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Shell脚本——位置参数

Shell脚本——位置参数 位置参数1、访问命令行1.1、确定参数个数1.2、shift—访问多个参数1.3、简单应用1.4、在Shell函数中使用位置参数2、批量处理位置参数3、一个更完整的应用4、总结1、访问命令行Shell提供了一组名为位置参数positionalparameters的变量其中包含了命令行上的各个单词。这些变量按照0~9分别命名可以通过下列方式展示#!/bin/bash# posit-param: 查看命令行参数的脚本echo \$0$0\$1$1\$2$2\$3$3\$4$4\$5$5\$6$6\$7$7\$8$8\$9$9这个简单的脚本显示了变量$0~$9的值。如果执行脚本的时候没有使用命令行参数则执行结果为[melinuxbox ~]$ posit-param$0/home/me/bin/posit-param$1$2$3$4$5$6$7$8$9就算没提供参数值$0始终出现在命令行中的第一项表示执行程序的路径。如果提供了参数值会看到下列执行结果[melinuxbox ~]$ posit-param a b c d$0/home/me/bin/posit-param$1a$2b$3c$4d$5$6$7$8$9注意能通过参数扩展访问的位置参数不止9个。要想指定第9个之后的参数将数字放入花括号中即可即10 、 {10}、10、{55}、${211}等。1.1、确定参数个数Shell还提供了变量$#其中包含了命令行中的参数个数#!/bin/bash# posit-param: 查看命令行参数的脚本echo Number of arguments:$#\$0$0\$1$1\$2$2\$3$3\$4$4\$5$5\$6$6\$7$7\$8$8\$9$9执行结果如下[melinuxbox ~]$ posit-param a b c d Number of arguments:4$0/home/me/bin/posit-param$1a$2b$3c$4d$5$6$7$8$91.2、shift—访问多个参数如果我们像下面这样为程序指定了大量参数那么会怎样[melinuxbox ~]$ posit-param * Number of arguments:82$0/home/me/bin/posit-param$1addresses.ldif$2bin$3bookmarks.html$4debian-500-i386-netinst.iso$5debian-500-i386-netinst.jigdo$6debian-500-i386-netinst.template$7debian-cd_info.tar.gz$8Desktop$9dirlist-bin.txt在该程序中通配符*被扩展成82个参数我们该怎么处理这么多参数Shell给出了解决之道尽管有些笨拙。每执行一次shift命令就将所有的参数“左移一个位置”​。实际上通过shift命令我们可以自始至终只和一个参数打交道除了$0​#!/bin/bash# posit-param2: 脚本显示所有参数count1while[[$#-gt0]];doechoArgument$count$1count$((count1))shiftdone每次执行shift$2的值就会移入$1然后$3的值移入2 依此类推。与此同时 2依此类推。与此同时2依此类推。与此同时#的值也会相应减一。在posit-param2程序中我们创建了一个循环评估所剩的参数数量只要数量不为0循环就继续执行。在每次循环中我们先显示当前参数然后增加变量count的值表示已处理过的参数数量最后执行shift将下一个参数的值移入$1。该程序的执行结果如下[melinuxbox ~]$ posit-param2 a b c d Argument1a Argument2b Argument3c Argument4d1.3、简单应用即便没有shift也能利用位置参数写出实用的程序。来看一个简单的文件信息程序#!/bin/bash# file-info: 简单文件信息程序PROGNAME$(basename$0)if[[-e$1]];thenecho-e\nFile Type:file$1echo-e\nFile Status:stat$1elseecho$PROGNAME: usage:$PROGNAMEfile2exit1fi该程序显示文件类型取自file命令和文件状态信息取自stat命令​。其中一处值得注意的地方是PROGNAME变量它的值是命令basename $0的执行结果。basename命令会删除路径名中的开头部分只留下文件的基础名称。在这个例子中basename删除了$0参数所包含的文件信息程序完整路径名的开头部分。这个值在构建如程序末尾的用法信息时能用得上。通过这种方法就算程序改了名消息也会自动调整显示正确的程序名称。1.4、在Shell函数中使用位置参数位置参数既可以向Shell脚本传递参数也可以向Shell函数传递参数。作为演示我们将file_info脚本改写成Shell函数file_info(){# file_info: 函数显示文件信息if[[-e$1]];thenecho-e\nFile Type:file$1echo-e\nFile Status:stat$1elseecho$FUNCNAME: usage:$FUNCNAMEfile2return1fi}现在如果包含Shell函数file_info的脚本以文件名作为参数调用该函数参数就会被传入函数。有了这种能力我们便可以编写出很多实用的Shell函数不仅能将其用在脚本中还能将其应用于.bashrc文件。注意PROGNAME变量被改成了Shell变量FUNCNAME。Shell会自动更新该变量以跟踪当前正在执行的Shell函数。$0始终包含的是命令行中第一项也就是程序名的完整路径并不包含Shell函数名。2、批量处理位置参数有时候批量处理所有位置参数更为实用。假设我们要为其他程序编写一个“包装器”​wrapper​这意味着需要创建一个脚本或Shell函数以简化其他程序的调用。包装器负责提供一系列晦涩的命令选项然后将参数列表传给低层程序。Shell为此提供了两个特殊参数*和两者均可扩展成完整的位置参数列表但其区别有些微妙。下表描述了这两个特殊参数。来看一个演示这些特殊参数用法的脚本示例#!/bin/bash# posit-params3: 演示$*和$的脚本print_params(){echo\$1$1echo\$2$2echo\$3$3echo\$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_paramswordwords with spaces在这个相当复杂的脚本中我们创建了两个参数分别是word和words with spaces将其传入函数pass_params。该函数将接收到的参数再传入函数print_params在这个过程中使用了4种不同的方法来处理特殊参数∗ 和 *和∗和。从脚本的执行结果中可以看出两者之间的差异[melinuxbox ~]$ posit-param3$*:$1word$2words$3with$4spaces$*:$1word words with spaces$2$3$4$:$1word$2words$3with$4spaces$:$1word$2words with spaces$3$4使用我们选择的参数∗ 和 *和∗和生成了4个单词word words with spaces$*生成了1个单词word words with spaces$生成了2个单词wordwords with spaces这符合我们的实际意图。从中得到的结论就是在Shell所提供的4种获取位置参数列表的不同方法中到目前为止$适用于大部分情况因为其保留了每个位置参数的整体性。为了保证安全性你应该坚持使用这种方法除非你有令人信服的原因。3、一个更完整的应用我们现在要继续完善前面的sys_info_page程序接下来要为其添加一些命令行选项。输出文件。该选项用于指定保存程序输出的文件名其形式可以是-f file或–file file。交互模式。该选项提示用户输出文件并判断指定文件是否已经存在。如果存在在覆盖已有文件之前会提示用户其形式为-i或–interactive。帮助。该选项使程序输出用法信息其形式为-h或–help。实现命令行处理的代码如下usage(){echo$PROGNAME: usage:$PROGNAME[-f file | -i]return}# 进程命令行选项interactivefilenamewhile[[-n$1]];docase$1in-f|--file)shiftfilename$1;;-i|--interactive)interactive1;;-h|--help)usageexit;;*)usage2exit1;;esacshiftdone首先添加Shell函数usage其用于在指定帮助选项或出现未知选项时显示用法信息。接下来我们开始处理循环。只要位置参数$1不为空循环就不会停止。在循环末尾使用shift命令向前移动位置参数确保该循环最终能够停止。在循环内部使用case语句检查当前位置参数看一看是否匹配所支持的某个选项。如果匹配则执行相应的操作。如果发现有未知选项则显示用法信息终止脚本并返回错误。-f选项的处理方式值得注意。如果发现有该选项会再执行一次shift并将-f选项的文件名参数移入位置参数$1中。实现交互模式的代码如下# 交互模式if[[-n$interactive]];thenwhiletrue;doread-pEnter name of output file: filenameif[[-e$filename]];thenread-p$filename exists. Overwrite? [y/n/q] case$REPLYinY|y)break;;Q|q)echoProgram terminated.exit;;*)continue;;esacelif[[-z$filename]];thencontinueelsebreakfidonefi如果变量interactive不为空则开启无限循环其中包含文件名提示以及后续的已有文件处理代码。如果指定的输出文件已存在会提示用户要么覆盖该文件要么更改文件名要么退出程序。如果用户选择覆盖已有文件执行break停止循环。注意case语句只检查用户选择的是覆盖文件还是退出程序其他选择会使循环继续执行再次向用户发出提示信息。为了实现指定输出文件名的功能我们必须先将现有的写页面代码page-writing code改为Shell函数稍后你就知道为什么要这么做了write_html_page(){cat-_EOF_htmlheadtitle$TITLE/title/headbodyh1$TITLE/h1p$TIMESTAMP/p$(report_uptime)$(report_disk_space)$(report_home_space)/body/html_EOF_return}# 输出HTML页if[[-n$filename]];theniftouch$filename[[-f$filename]];thenwrite_html_page$filenameelseecho$PROGNAME: Cannot write file $filename2exit1fielsewrite_html_pagefi处理-f选项的逻辑位于上述代码的结尾部分。其中我们测试了文件是否存在如果存在再测试文件是否可写。为此执行touch命令并检验生成的文件是否为普通文件。这两处测试分别考虑到了输入的路径是否无效touch会执行失败以及如果文件已存在其是否为普通文件。可以看到实际生成页面时调用了write_html_page函数该函数的输出被传入标准输出如果filename变量为空或重定向到指定文件。因为HTML代码有两个可能的目的地因此有必要将write_html_page转换为Shell函数以避免冗余代码。4、总结有了位置参数我们现在能够写出功能更强的脚本。例如对于重复性任务我们可以利用位置参数编写实用Shell函数并将其放入用户的.bashrc文件。sys_info_page程序已经变得越来越复杂下面给出了完整的代码并着重标出了最新的改动#!/bin/bash# sys_info_page: 程序输出系统显示页PROGNAME$(basename$0)TITLESystem Information Report For$HOSTNAMECURRENT_TIME$(date%x %r %Z)TIMESTAMPGenerated$CURRENT_TIME, by$USERreport_uptime(){cat-_EOF_h2System Uptime/h2pre$(uptime)/pre_EOF_return}report_disk_space(){cat-_EOF_h2Disk Space Utilization/h2pre$(df-h)/PRE_EOF_return}report_home_space(){if[[$(id-u)-eq0]];thencat-_EOF_h2Home Space Utilization(All Users)/h2pre$(du-sh/home/*)/pre_EOF_elsecat-_EOF_h2Home Space Utilization($USER)/h2pre$(du-sh$HOME)/pre_EOF_fireturn}usage(){echo$PROGNAME: usage:$PROGNAME[-f file | -i]return}write_html_page(){cat-_EOF_htmlheadtitle$TITLE/title/headbodyh1$TITLE/h1p$TIMESTAMP/p$(report_uptime)$(report_disk_space)$(report_home_space)/body/html_EOF_return}# 进程命令行选项interactivefilenamewhile[[-n$1]];docase$1in-f|--file)shiftfilename$1;;-i|--interactive)interactive1;;-h|--help)usageexit;;*)usage2exit1;;esacshiftdone# 交互模式if[[-n$interactive]];thenwhiletrue;doread-pEnter name of output file: filenameif[[-e$filename]];thenread-p$filename exists. Overwrite? [y/n/q] case$REPLYinY|y)break;;Q|q)echoProgram terminated.exit;;*)continue;;esacelif[[-z$filename]];thencontinueelsebreakfidonefi# 输出HTML页if[[-n$filename]];theniftouch$filename[[-f$filename]];thenwrite_html_page$filenameelseecho$PROGNAME: Cannot write file $filename2exit1fielsewrite_html_pagefi该程序还不完善有几处地方仍有改善的空间。
返回列表