写出健壮的 Bash 脚本

许多人用shell脚本完成一些简单任务,而且变成了他们生命的一部分。不幸的是,shell脚本在运行异常时会受到非常大的影响。在写脚本时将这类问题最小化是十分必要的。本文中我将介绍一些让bash脚本变得健壮的技术。

使用set -u

你因为没有对变量初始化而使脚本崩溃过多少次?对于我来说,很多次。

chroot=$1...rm -rf $chroot/usr/share/doc

如果上面的代码你没有给参数就运行,你不会仅仅删除掉chroot中的文档,而是将系统的所有文档都删除。那你应该做些什么呢?好在bash提供了set -u,当你使用未初始化的变量时,让bash自动退出。你也可以使用可读性更强一点的set -o nounset

david% bash /tmp/shrink-chroot.sh

$chroot=

david% bash -u /tmp/shrink-chroot.sh

/tmp/shrink-chroot.sh: line 3: $1: unbound variable

david%

使用set -e

你写的每一个脚本的开始都应该包含set -e。这告诉bash一但有任何一个语句返回非真的值,则退出bash。使用-e的好处是避免错误滚雪球般的变成严重错误,能尽早的捕获错误。更加可读的版本:set -o errexit

使用-e把你从检查错误中解放出来。如果你忘记了检查,bash会替你做这件事。不过你也没有办法使用$?来获取命令执行状态了,因为bash无法获得任何非0的返回值。你可以使用另一种结构:

command

if [ “$?”-ne 0]; then echo “command failed”; exit 1; fi

可以替换成:

command || { echo “command failed”; exit 1; }

或者使用:

if ! command; then echo “command failed”; exit 1; fi

如果你必须使用返回非0值的命令,或者你对返回值并不感兴趣呢?你可以使用 command || true ,或者你有一段很长的代码,你可以暂时关闭错误检查功能,不过我建议你谨慎使用。

set +e

command1

command2

set -e

相关文档指出,bash默认返回管道中最后一个命令的值,也许是你不想要的那个。比如执行 false | true 将会被认为命令成功执行。如果你想让这样的命令被认为是执行失败,可以使用 set -o pipefail

程序防御 – 考虑意料之外的事

你的脚本也许会被放到“意外”的账户下运行,像缺少文件或者目录没有被创建等情况。你可以做一些预防这些错误事情。比如,当你创建一个目录后,如果父目录不存在,mkdir 命令会返回一个错误。如果你创建目录时给mkdir命令加上-p选项,它会在创建需要的目录前,把需要的父目录创建出来。另一个例子是 rm 命令。如果你要删除一个不存在的文件,它会“吐槽”并且你的脚本会停止工作。(因为你使用了-e选项,对吧?)你可以使用-f选项来解决这个问题,在文件不存在的时候让脚本继续工作。 

准备好处理文件名中的空格

有些人从在文件名或者命令行参数中使用空格,你需要在编写脚本时时刻记得这件事。你需要时刻记得用引号包围变量。

if [ $filename = “foo” ];

$filename变量包含空格时就会挂掉。可以这样解决:

if [ “$filename” = “foo” ];

使用$@变量时,你也需要使用引号,因为空格隔开的两个参数会被解释成两个独立的部分。

david% foo() { for i in $@; do echo $i; done }; foo bar “baz quux”

bar

baz

quux

david% foo() { for i in “$@”; do echo $i; done }; foo bar “baz quux”

bar

baz quux

我没有想到任何不能使用“$@”的时候,所以当你有疑问的时候,使用引号就没有错误。

如果你同时使用find和xargs,你应该使用 -print0 来让字符分割文件名,而不是换行符分割。

 david% touch “foo bar”

david% find | xargs ls

ls: ./foo: No such file or directory

ls: bar: No such file or directory

david% find -print0 | xargs -0 ls

./foo bar

设置的陷阱

当你编写的脚本挂掉后,文件系统处于未知状态。比如锁文件状态、临时文件状态或者更新了一个文件后在更新下一个文件前挂掉。如果你能解决这些问题,无论是 删除锁文件,又或者在脚本遇到问题时回滚到已知状态,你都是非常棒的。幸运的是,bash提供了一种方法,当bash接收到一个UNIX信号时,运行一个 命令或者一个函数。可以使用trap命令。

trap command signal [signal …]

你可以链接多个信号(列表可以使用kill -l获得),但是为了清理残局,我们只使用其中的三个:INTTERMEXIT。你可以使用-as来让traps恢复到初始状态。

信号描述

 

INT

Interrupt – 当有人使用Ctrl-C终止脚本时被触发

TERM

Terminate – 当有人使用kill杀死脚本进程时被触发

EXIT

Exit – 这是一个伪信号,当脚本正常退出或者set -e后因为出错而退出时被触发

 

 

 

 

当你使用锁文件时,可以这样写:

if [ ! -e $lockfile ]; then

touch $lockfile

critical-section

rm $lockfile

else

echo “critical-section is already running”

fi

当最重要的部分(critical-section)正在运行时,如果杀死了脚本进程,会发生什么呢?锁文件会被扔在那,而且你的脚本在它被删除以前再也不会运行了。解决方法:

if [ ! -e $lockfile ]; then

trap ” rm -f $lockfile; exit” INT TERM EXIT

touch $lockfile

critical-section

rm $lockfile

trap – INT TERM EXIT

else

echo “critical-section is already running”

fi

现在当你杀死进程时,锁文件一同被删除。注意在trap命令中明确地退出了脚本,否则脚本会继续执行trap后面的命令。

竟态条件 (wikipedia)

在上面锁文件的例子中,有一个竟态条件是不得不指出的,它存在于判断锁文件和创建锁文件之间。一个可行的解决方法是使用IO重定向和bash的noclobber(wikipedia)模式,重定向到不存在的文件。我们可以这么做:

if ( set -o noclobber; echo “$$” > “$lockfile”) 2> /dev/null;

then

trap ‘rm -f “$lockfile”; exit $?’ INT TERM EXIT

critical-section

rm -f “$lockfile”

trap – INT TERM EXIT

else

echo “Failed to acquire lockfile: $lockfile”

echo “held by $(cat $lockfile)”

fi

更复杂一点儿的问题是你要更新一大堆文件,当它们更新过程中出现问题时,你是否能让脚本挂得更加优雅一些。你想确认那些正确更新了,哪些根本没有变化。比如你需要一个添加用户的脚本。

add_to_passwd $user

cp -a /etc/skel /home/$user

chown $user /home/$user -R

当磁盘空间不足或者进程中途被杀死,这个脚本就会出现问题。在这种情况下,你也许希望用户账户不存在,而且他的文件也应该被删除。

rollback() {

del_from_passwd $user

if [ -e /home/$user ]; then

rm -rf /home/$user

fi

exit

}

 

trap rollback INT TERM EXIT

add_to_passwd $user

 

cp -a /etc/skel /home/$user

chown $user /home/$user -R

trap – INT TERM EXIT

在脚本最后需要使用trap关闭rollback调用,否则当脚本正常退出的时候rollback将会被调用,那么脚本等于什么都没做。

保持原子化

又是你需要一次更新目录中的一大堆文件,比如你需要将URL重写到另一个网站的域名。你也许会写:

for file in $(find /var/www -type f -name “*.html”); do

perl -pi -e ‘s/www.example.net/www.example.com/’ $file

done

如果修改到一半是脚本出现问题,一部分使用www.example.com,而另一部分使用www.example.net。你可以使用备份和trap解决,但在升级过程中你的网站URL是不一致的。

解决方法是将这个改变做成一个原子操作。先对数据做一个副本,在副本中更新URL,再用副本替换掉现在工作的版本。你需要确认副本和工作版本目录在同一个磁盘分区上,这样你就可以利用Linux系统的优势,它移动目录仅仅是更新目录指向的inode节点。

cp -a /var/www /var/www-tmp

for file in $(find /var/www-tmp -type -f -name “*.html”); do

perl -pi -e ‘s/www.example.net/www.example.com/’ $file

done

mv /var/www /var/www-old

mv /var/www-tmp /var/www

这意味着如果更新过程出问题,线上系统不会受影响。线上系统受影响的时间降低为两次mv操作的时间,这个时间非常短,因为文件系统仅更新inode而不用真正的复制所有的数据。

这种技术的缺点是你需要两倍的磁盘空间,而且那些长时间打开文件的进程需要比较长的时间才能升级到新文件版本,建议更新完成后重新启动这些进程。对于 apache服务器来说这不是问题,因为它每次都重新打开文件。你可以使用lsof命令查看当前正打开的文件。优势是你有了一个先前的备份,当你需要还原 时,它就派上用场了。

NoSQL 崛起的六大原因

NoSQL产品越来越火,NoSQL产品通常以其高性能,强扩展性和高容错性为大家所称道,我们在问为什么NoSQL会流行起来的时候,或许应该问一下为什么这些功能会变得这么重要。

  • We’re dealing with much more data. Although advances in storage capacity and CPU speed have allowed the databases to keep pace, we’re in a new era where size itself is an important part of the problem, and any significant database needs to be distributed.
  • 由于我们需要处理的数据集越来越大,其存储量已经远远超过了单机的容量,数据处理的需求也远远超过了单机CPU的运算能力。所以我们需要分布式的解决方案。
  • We require sub-second responses to queries. In the ’80s, most database queries could run overnight as batch jobs. That’s no longer acceptable. While some analytic functions can still run as overnight batch jobs, we’ve seen the web evolve from static files to complex database-backed sites, and that requires sub-second response times for most queries.
  • 我们对数据提供速度的要求越来越高,在80年代,可能很多运算都需要跑一整晚。但是这种事情放在现在就变得不可接受了。对于复杂的统计分析我们可以忍受,但是对于网站应用来说,快速响应是必须的。
  • We want applications to be up 24/7. Setting up redundant servers for static HTML files is easy, but a database replication in a complex database-backed application is another.
  • 我们需要提供7×24的服务。如果你的网站只有一个静态页,那估计问题不大,只需要做好WebServer的容错性就行了。而如果你是一个背后有数据库,有缓存的动态网站,那你就必须做好数据层的容错及自动故障迁移。
  • We’re seeing many applications in which the database has to soak up data as fast (or even much faster) than it processes queries: in a logging application, or a distributed sensor application, writes can be much more frequent than reads. Batch-oriented ETL (extract, transform, and load) hasn’t disappeared, and won’t, but capturing high-speed data flows is increasingly important.
  • 很多应用场景需要数据层提供更高的写性能和数据吞吐。比如日志型应用,对写性能的要求可能非常高,当写性能成为瓶颈时,通常我们很难难过升级单机配置来解决。所以分布式的需求在这里变得也很重要。
  • We’re frequently dealing with changing data or with unstructured data. The data we collect, and how we use it, grows over time in unpredictable ways. Unstructured data isn’t a particularly new feature of the data landscape, since unstructured data has always existed, but we’re increasingly unwilling to force a structure on data a priority.
  • 我们对非结构化数据的存储和处理需求日增,在这个变化的世界,互联网领域的应用可能越来越难像软件开发一样,去预先写义各种数据结构。
  • We’re willing to sacrifice our sacred cows. We know that consistency and isolation and other properties are very valuable, of course. But so are some other things, like latency and availability and not losing data even if our primary server goes down. The challenges of modern applications make us realize that sometimes we might need to weaken one of these constraints in order to achieve another.
  • 我们的应用场景对一致性,隔离性以及其它一些事务特性的需求可能越来越低,相反的,我们对性能,对扩展性的需求可能越来越高。于是在新的需求下,我们必须做出抉择,放弃一些我们习惯了的优秀功能,去获取一些我们需要的新的特性。

来源:bigdatadiary.com

亲密敌人?苹果今年对三星采购额将增至110亿美元

虽然苹果和三星最近打的不可开交,但这仍然不妨碍这对冤家做生意。套用流行的话讲,叫做“亲密敌人”。

3月14日消息,国外媒体报道,虽然苹果可能减少对三星部件供应的依赖,但今年苹果对三星的部件订单却可能达到历年来的最大规模。三星一位高管表示,苹果今年的订单额可能从2011年的78亿美元升至2012年的110亿美元。目前三星的订单额在97亿美元左右,但由于苹果计划发布更小型的iPad,可能 使用7.85英寸屏幕,并力图提高使用三星固态硬盘的MacBook Air PC销量,因此预计到今年底苹果对三星的订单额将增至110亿美元。

虽然苹果和三星之间存在专利纠纷,双方正展开诉讼,但与此同时两家公司仍保持着紧密的供应商/客户关系,三星供应的许多部件都是苹果许多核心产品的组件,其中包括NAND闪存、移动处理器、显示器。而且现在三星似乎也在增加对新显示屏技术的投资,希望增强组件供应地位。

内部人士透露,苹果可能还有意选择OLED技术,而三星在此方面投入了大量资金,希望成为全球首家向苹果供应OLED面板的厂商。虽然苹果有意使用三星的OLED技术,但对三星OLED显示屏的产能存有疑问。

与此同时,苹果2013和2014年对三星的采购额可能进一步上升,而2014年是苹果与三星当前供应协议的最后一年。苹果采购额的上升对三星而言可谓喜忧参半,一方面这会增加三星的收入来源,但同时也表明三星需要进一步加大力度发展自己的产品。

16 款 HTC 智能手机将升级至 Android 4.0

近日,HTC在其官方博客上宣布,至少有16款智能手机将升级至最新的Android 4.0版本,其中包括Droid Incredible 2、Amaze 4G、Desire S和Desire HD等。

在推迟很长一段时间后,三星最终宣布其Galaxy S II将迎来Android 4.0升级。同样,HTC这次升级的具体时间暂时仍无法确定,很大程度上取决于各市场的运营商。

具体而言,以下是将进行升级的设备名单:

  • DROID Incredible 2 by HTC
  • HTC Amaze 4G
  • HTC Desire S
  • HTC Desire HD
  • HTC EVO 3D
  • HTC EVO Design 4G
  • HTC Incredible S
  • HTC Sensation
  • HTC Sensation XL
  • HTC Sensation 4G
  • HTC Sensation XE
  • HTC Raider
  • HTC Rezound
  • HTC Rhyme
  • HTC Thunderbolt
  • HTC Vivid

Via TNW

Intel:用先进工艺死拼ARM

Intel制造业务主管和新任COO布赖恩·科兹安尼克(Brian Krzanich)今天表示,为了满足智能机和平板电脑芯片预期需求,他已经调整好了供应链。科兹安尼克今年1月份被公司提拔,并被视为未来CEO接班人的热门人选。他表示,其专注点一直在于缩短旗下先进工厂的周转时间,改善提高已经成为分散移动市场的关键步骤。

我们将开始看到越来越多的产能转向移动市场,比如手机、平板电脑和其它设备。”科兹安尼克称。

他此前已经负责Intel制造业务,这其中包括了针对2012年的125亿美元资本支出预算以及公司大部分日常运营。作为COO,他将负责Intel IT和人力资源部门。

Intel目前在智能机和平板电脑芯片市场已经被远远甩下,该市场现在被英国ARM公司所统治,高通和德州仪器均生产ARM架构移动芯片,不过 Intel新款Medfield芯片已经被联想、摩托罗拉移动等其他智能机制造商采用,作为未来少数新款智能机的处理器,这被华尔街视为一个好的开始。

Intel认定他们在制造技术的领先地位将帮助他们从竞争对手那里赢得更多份额,并且开始加速使用最先进工厂生产移动芯片。作为制造业的主管,科兹安尼克缩短了建设先进工厂的时间,并使之投产。

作为在Intel工作30年的资深高管,科兹安尼克表示,过去五年,他将元件制造时间缩短了一半,从接到订单到发货的时间也缩短了近一半。“如果问我为制造业带去了什么,那就是速度和敏捷,”他说,“这正是PC业和手机业所需要的。”

Intel CEO保罗·欧德宁(Paul Otellini)在2005年前一直担任COO职位。在科兹安尼克被晋升为COO前,去年被任命为执行董事长的安迪·布莱恩特(Andy Bryant)一直担任部分COO职责。(文/新浪科技)

Intel:已为移动市场做好生产准备