未加星标

[译]Bash Shell高级编程如何提高你的工作效率(下)

字体大小 | |
[系统(linux) 所属分类 系统(linux) | 发布者 店小二03 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

原文: Advancing in the Bash Shell

译者:杰微刊兼职翻译刘晓鹏

[译]Bash Shell高级编程如何提高你的工作效率(上)

别名

使用别名就类似于创建你自己的命令。你自己决定你想要输入什么?执行什么命令?别名可以在几个不同的地方定义,~/.bashrc ~/.bash_profile ~/.profile以及~/.aliases等。事实上,你也真的没有必要把它们放在同一个地方。在不同的shell下,使用不同的文件,从而产生不同的行为,不过这部分内容已超出了本文的范围。为了讨论这个问题,我们在~/.bash_profile中进行设置(登陆shell所使用)。

在这个文件里面,通常是在最底部,我加入了我的别名。下面是几个示例:

alias ud=’aptitude update && aptitude dist-upgrade’
alias ls=’ls color=auto’
alias mroe=’less’
alias H=’kill -HUP’
alias ssh=’ssh -AX’
alias webshare=’python -c “import SimpleHTTPServer;SimpleHTTPServer.test()”‘

最后一个可能有点绕,但是它提供一个很好的示例来展示别名的优点。一个完整的长字符串命令变的非常简短,这样很容易记住。

括号扩展

每个人都曾执行过如下命令来实现文件的快速备份:

$cp filename filename-old
$cp filename-old filename 这看起非常简单,我们怎么能够让它更高效呢?让我们看一个例子: $cp filename{,-old}
$cp filename{-old,}
$cp filename{-v1,-v2}

这个列子前两条命令,这与我的前一个例子的功能完全一致,但是输入的更少。第一个例子是将名为filename的文件拷贝一份到filename-old中,第二个例子是将名为filename-old的文件拷贝到filename中。

第三条命令可能比前两条命令能更清晰表示真实的场景。在第三条命令中,我们将一个名为filename-v1的文件拷贝到名为filename-v2的文件中。在这种情况下的大括号({}将告诉bash进行“括号扩展”。前缀(我们用例中的filename)会添加到大括号中通过逗号分隔的字符串列表的前面,这样就会为列表中的每个字符串创建一个新的单词。所以,第三条会被扩展为:

$cp filename-v1 filename-v2<br />

括号扩展可以出现在你命令中的任何地方,也可以在同一行中多次出现,甚至嵌套出现。括号扩展表达式从左至右进行运算。看几个例子: $touch a{1,2,3}b
$touch {p2,pv,av,}p
$ls /usr/{,local/}{,s}bin/jojo 第一个例子将创建a1b,a2b,a3b三个文件,在这个示例中,前缀和后缀都添加到了大括号中的每个字符串中。第二个例子没有前缀,所以只会将后缀添加到每个字符串中,因此会创建p2p,pvp,avp和p四个文件。第二个例子中的最后一个字符串是空,所以p不会拼接任何内容,因此只有p。第三个例子展示了同一行中多个括号的情况,它会扩充为:

$ls /usr/bin/jojo /usr/sbin/jojo /usr/local/bin/jojo /usr/local/sbin/jojo

下面是嵌套括号扩展的示例。

$apt-get remove --purge ppp{,config,oe{,conf}}

shell会将其扩展为:

$apt-get remove --purge ppp pppconfig pppoe pppoeconf

前缀“ppp”将会被前置到(从左至右):空字符({,),config,然后第二个扩展开始执行,生成一个新的前缀,"oe"将被前置到空字符({,),然后到conf,这两个再加上最原始的前缀。

更多关于括号扩展的内容,包括嵌套的一些例子,可以阅读bash的参考手册。

词的修饰符

在Bash Shell高级编程的第一部分,我们学习了:p,:p是用于打印命令,但是不执行命令。:p 是“修饰符”的一个示例,它还有几个兄弟。下面是bash的参考手册中的缩写列表:

h

删除文件名组件的尾部,只留下头部。

t

删除文件名组件的头部,只留下尾部。

r

删除.xxx形式的后缀,只留下基本名称。

e

除了后缀,删除所有剩余部分。

假定我们正在读一个深度嵌套目录结构中的一个文件。当我编辑完该文件时,我意识到在同目录下还有其他操作,如果我在该目录,会更容易完成。这种情况下,我可以使用:h来帮忙。 $links /usr/local/share/doc/3dm/3DM_help.html
$cd !$:h
$links !-2$:t

我们的老朋友 !$ 回来了,并且通过:h来修饰。第二个命令告诉bash切换到!$,或者说上一条命令的最后一个参数,通过:h来修饰,表示删除字符串中的文件名,只留下目录。

第三个命令看起让人抓狂,但其实很简单。!-2表示之前的第N(这里是2)条命令。$ 表示命令的最后一个参数,而:t表示删除参数中的目录。所以,总的来说:执行命令时使用最后一条命令的最后一个参数,并且删除该参数的路径或3DM_help.html。没什么大不了的,对吧?

在我们的下一个例子中,我们从网上下载了一个jar包。我们检测其是否会为它的文件创建一个目录,结果发现没有。为了不让当前的目录混乱,我们为它创建一个目录。 $wgethttp://www.example.com/path/to/jubby.tgz
$tar tzvf jubby.tgz
[output]
$mkdir !$:r

第三个命令将会创建一个名为'jubby'的目录

修饰符也可以叠加。在下一个例子中,我们下载一个文件到/tmp目录下,然后在为tar文件中的内容在/usr/local/src中创建一个目录。 $cd /tmp
$wgethttp://www.example.com/path/KickassApplicationSuite.tar.gz
$cd /usr/local/src/
$mkdir !-2$:t:r:r
{creates directory called 'KickassApplicationSuite'}
$cd !$
$tar xvzf /tmp/!-4$:t

前面三条命令很正常,没有使用替换。但是,第四条命令看起来很难理解。我们知道!-2之前最近的第二条命令,$表示命令的最后一个参数。我们也知道:t将删除参数中的路径部分(即使在这里有"http://")。我们也知道:r将删除参数中文件的扩展名,但是这里调用了两次,因为这里有两个扩展名(第一个:r删除.gz,第二个:r删除.tar)。然后我们切换到这个目录(再一次用到了!$,表示上一条命令的最后一个参数,这里表示mkdir的参数,也就是‘KickassApplicationSuite’),然后,我们解压文件,!-4$表示之前最近的第四条命令的最后一个参数,该参数由:t修饰表示删除路径,因为我们已经在路径中加了/tmp。所以最后一条明显就变成了tar xvzf /tmp/KickassApplicationSuite.tar.gz.

还有一个修饰符可以用来进行替换。:s可以做类似于扬抑符 (^)的事情,做一些简单的线性替换。 $vi /etc/X11/XF86config
$!!:s/config/Config-4/ 我们知道!!表示上一条命令。:s修饰前一条命令,表示将:s第一个参数替换为:s第二个参数。我们的例子中使用/来划分两个参数,实际上任何非空字符都可以使用。需要重点注意的是,类似于扬抑符的替换,:s的替换页只替换字符串中的第一个。如果想替换字符串中所有匹配的,你必须在:s中指定修饰符:g。 $mroe file1 ; mroe file2
$!!:gs/mroe/more

第二个命令的替换(:s)more是进行了全局(:g)mroe的替换。提示::g也可以用在扬抑符(^)上。

本教程中最后一个要看的修饰符是&。&代表重复之前的替换。我们假定通过ls命令来检查文件的属性。

$ls -lh myfile otherfile anotherfile
$!!:s/myfile/myfile.old/ 看起来很简单,:s这一步是将myfile修改成myfile.old,所以最后我们执行的是ls -lh myfile.old myfile2 myfile3。在:s中,&只是一种简写,代表第一个参数。下面是上一个例子的等价形式: $ls -lh myfile otherfile anotherfile
$!!:s/myfile/&.old/

&有一点奇怪,因为它在shell中存在不同上下文环境。请记住这里是使用&作为一个修饰符。

Bash函数

在上文中我们学习了一点关于别名的东西。别名是简单、静态和直接替换的。这不是说就不能有一个非常高级和复杂的别名,而是无论别名有多复杂,shell都是进行简单的替换,即从^x到^y。shell的函数类似别名,但是可以包含逻辑和位置参数,从而使得它更强大。

什么是位置参数?很高兴你能问这个问题。位置参数标示一个参数的位置是非常重要的。例如,下面的函数中,需要拷贝的目录必须是第一个参数,目标目录必须是第二个参数。

function treecp { tar cf - "${1}" | (cd "${2}" ; tar xpf -) ; };

当然可以(也很容易)写出一个可以接受任何顺序的函数,但是在许多情况下,这样做是没有意义的。想象一下,如果cp命令可以接收任何顺序的参数,你就不得不使用开关来表示目标文件是哪个。

让我们看一下上述的例子。为了让bash知道你声明了一个函数,你必须在你函数开始指定关键字function。函数的第一个参数是你声明的函数的名称。在这个例子中时treecp。接下来的字符 { 表示一个shell列表。在这个例子中的列表是一个命令列表。在大括号后,开始定义该函数的逻辑,直到函数被另一半大括号()}关闭。

只要你知道两个参数的含义,该函数的逻辑就非常简单了。"${1}"表示你命令的第一个参数,"${2}" 表示第二个参数,以此类推。这就是位置参数。它们的编号表示它们的位置。你可能会想"${0}"是命令自身的名称,但是实际上是当前“环境”的名称。在shell脚本中,它表示shell脚本的名称。在你的交互shell中,它表示带有参数的shell名称。如果你想获取你所在函数的名称,可以使用${FUNCNAME}。

所以,如果要使用treecp函数,我们必须提供两个参数,源目录和目标目录:

$treecp dmr ~/public_html

dmr变成了"${1}", ~/public_html扩展为/home/whomever/public_html,转变为"${2}"。

如果用户忘记加一个或者两个参数,将会发生什么?函数怎么知道它是否应该继续执行?上述的函数是不知道的。它只是简单以它的方式来执行,而不管它接收了几个参数。我们加一些逻辑来确保在命令执行前的参数是我们所期望的。

在做这个之前,我们需要学习一些其他命令运行时变量值的知识,(类似"${1}")。"${#}"变量等价于给定命令参数的数量。例如: $function myfunc { echo "${#}" ; } ;
$myfunc foo bar taco jojo
[output is '4']
$myfunc *
[output is the same as 'ls | wc -l']
$myfunc
[output is '0'] 因此,现在我们可以知道传递给命令(在这种情况下是一个函数)的参数有多少个了,我们现在可以判断,如果接收到了两个必要参数,命令就可以正常执行。还有一种情况,这些参数都是垃圾数据,比如含有错别字或目录不存在,但不幸的是,函数不能替你思考这些。:) function treecp {
if [ "${#}" != 2 ] ; then
echo "Usage: treecp source destination";
return 1;
else
tar cf - "${1}" | (cd "${2}" ; tar xpf -) ;
fi ;
}; 我已经使用[(又叫测试)来判断应用的参数个数是不是预期的两个。如果多于或者少于两个参数,函数将打印出一条有用的信息,并将"${?}"的值设为1."${?}"叫做返回代码。我之后会小讨论一下返回代码的问题。如果有两个参数,命令运行时将使用第一个参数作为 tar cf - 的参数,第二个参数作为cd的参数。更多关于[的信息,请阅读参考手册(man [。]。 好了,位置参数还是很有意思的,但是如果我并不关心的位置,我需要将所有传递给函数参数都传递给命令,该如何处理?"${*}"正是你要寻找的答案。 $function n { echo "${*}">> ~/notes; };
$ndo the dumb things I gotta do, touch the puppet head.

无论你传递多少个单词给函数n,它们最终都会追加到我家目录下的notes文件的末尾。当以这种方式写入notes时,请注意避免是用shell特殊的字符。

上面,我们指定了1作为一个错误状态的返回代码。在什么情况下应该返回什么数字,这没有一个统一的规则,但有一些常用的返回代码,你可能想使用或至少应该知道。0(0)是常用的表示一个任务执行成功。1(1),(或任何非零数字)通常被用来表示一个错误状态。

如果一个函数或者shell脚本相当复杂,作者可以选择使用任何数字作为错误代码来代表不同的异常。例如,返回代码28时,可能意味着你的脚本无法在某个目录下创建一个文件,而返回代码29时,则表示脚本在调用wget下载一个文件时收到了一个错误代码。返回代码对逻辑的帮助比对人更大。不要忘了

本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统

分页:12
转载请注明
本文标题:[译]Bash Shell高级编程如何提高你的工作效率(下)
本站链接:http://www.codesec.net/view/482748.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(linux) | 评论(0) | 阅读(32)