??xml version="1.0" encoding="utf-8" standalone="yes"?> vim /etc/nginx/conf.d/nagios.conf #Ҏ自己的nginx启动位置自行调整 许多人用shell脚本完成一些简单Q务,而且变成了他们生命的一部分。不q的是,shell脚本在运行异常时会受到非常大的媄响。在写脚本时这c问题最化是十分必要的。本文中我将介绍一些让bash脚本变得健壮的技术? 你因为没有对变量初始化而脚本崩溃q多次Q对于我来说Q很多次? 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。这告诉bash一但有M一个语句返回非真的|则退出bash。?e的好处是避免错误滚雪球般的变成严重错误,能尽早的捕获错误。更加可ȝ版本Q?em>set -o errexit 使用-e把你从检查错误中解放出来。如果你忘记了检查,bash会替你做qg事。不q你也没有办法?em>$?来获取命令执行状态了Q因为bash无法获得M?的返回倹{你可以使用另一U结构: 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 如果你必M用返回非0值的命oQ或者你对返回值ƈ不感兴趣呢?你可以?command || true Q或者你有一D很长的代码Q你可以暂时关闭错误查功能,不过我徏议你谨慎使用? set +e command1 command2 set -e 相关文档指出Qbash默认q回道中最后一个命令的|也许是你不想要的那个。比如执?false | true 会被认为命令成功执行。如果你惌q样的命令被认ؓ是执行失败,可以使用 set -o pipefail 你的脚本也许会被攑ֈ“意外”的̎户下q行Q像~少文g或者目录没有被创徏{情c你可以做一些预防这些错误事情。比如,当你创徏一个目录后Q如果父目录不存在,mkdir 命o会返回一个错误。如果你创徏目录时给mkdir命o加上-p选项Q它会在创徏需要的目录前,把需要的父目录创建出来。另一个例子是 rm 命o。如果你要删除一个不存在的文Ӟ它会“吐槽”q且你的脚本会停止工作。(因ؓ你用了-e选项Q对吧?Q你可以使用-f选项来解册个问题,在文件不存在的时候让脚本l箋工作? 有些Z在文件名或者命令行参数中用空|你需要在~写脚本时时刻记得这件事。你需要时刻记得用引号包围变量? if [ $filename = "foo" ]; ?em>$filename变量包含I格时就会挂掉。可以这栯冻I if [ "$filename" = "foo" ]; 使用$@变量Ӟ你也需要用引P因ؓI格隔开的两个参C被解释成两个独立的部分? 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 我没有想CQ何不能?em>"$@"的时候,所以当你有疑问的时候,使用引号没有错误? 如果你同时用find和xargsQ你应该使用 -print0 来让字符分割文g名,而不是换行符分割? 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 当你~写的脚本挂掉后Q文件系l处于未知状态。比如锁文g状态、时文件状态或者更C一个文件后在更C一个文件前挂掉。如果你能解册些问题,无论?删除锁文Ӟ又或者在脚本遇到问题时回滚到已知状态,你都是非常棒的。幸q的是,bash提供了一U方法,当bash接收C个UNIX信号Ӟq行一?命o或者一个函数。可以?strong>trap命o? trap command signal [signal ...] 你可以链接多个信P列表可以使用kill -l获得Q,但是Z清理D局Q我们只使用其中的三个:INTQ?em>TERM?em>EXIT。你可以使用-as来让traps恢复到初始状态? Interrupt - 当有Z用Ctrl-Cl止脚本时被触发 Terminate - 当有Z用kill杀死脚本进E时被触? Exit - q是一个伪信号Q当脚本正常退出或者set -e后因为出错而退出时被触? 当你使用锁文件时Q可以这样写Q? if [ ! -e $lockfile ]; then touch $lockfile critical-section rm $lockfile else echo "critical-section is already running" fi 当最重要的部?critical-section)正在q行Ӟ如果杀M脚本q程Q会发生什么呢Q锁文g会被扔在那,而且你的脚本在它被删除以前再也不会运行了。解x法: 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 现在当你杀死进E时Q锁文g一同被删除。注意在trap命o中明地退Z脚本Q否则脚本会l箋执行trap后面的命令? 在上面锁文g的例子中Q有一个竟态条件是不得不指出的Q它存在于判断锁文g和创建锁文g之间。一个可行的解决Ҏ是用IO重定向和bash的noclobber(wikipedia)模式Q重定向C存在的文件。我们可以这么做Q? 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 更复杂一点儿的问题是你要更新一大堆文gQ当它们更新q程中出现问题时Q你是否能让脚本挂得更加优雅一些。你想确认那些正更CQ哪些根本没有变化。比如你需要一个添加用L脚本? add_to_passwd $user cp -a /etc/skel /home/$user chown $user /home/$user -R 当磁盘空间不x者进E中途被杀死,q个脚本׃出现问题。在q种情况下,你也许希望用戯̎户不存在Q而且他的文g也应该被删除? 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调用Q否则当脚本正常退出的时候rollback会被调用,那么脚本{于什么都没做? 又是你需要一ơ更新目录中的一大堆文gQ比如你需要将URL重写到另一个网站的域名。你也许会写Q? for file in $(find /var/www -type f -name "*.html"); do perl -pi -e 's/www.example.net/www.example.com/' $file done 如果修改C半是脚本出现问题Q一部分使用www.example.comQ而另一部分使用www.example.net。你可以使用备䆾和trap解决Q但在升U过E中你的|站URL是不一致的? 解决Ҏ是将q个改变做成一个原子操作。先Ҏ据做一个副本,在副本中更新URLQ再用副本替换掉现在工作的版本。你需要确认副本和工作版本目录在同一个磁盘分ZQ这样你可以利用Linuxpȝ的优势,它移动目录仅仅是更新目录指向的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 q意味着如果更新q程出问题,U上pȝ不会受媄响。线上系l受影响的时间降低ؓ两次mv操作的时_q个旉非常短,因ؓ文gpȝ仅更新inode而不用真正的复制所有的数据? q种技术的~点是你需要两倍的盘I间Q而且那些长时间打开文g的进E需要比较长的时间才能升U到新文件版本,更新完成后重新启动这些进E。对?apache服务器来说这不是问题Q因为它每次都重新打开文g。你可以使用lsof命o查看当前正打开的文件。优势是你有了一个先前的备䆾Q当你需要还?Ӟ它就z上用场了? Windows下调用程?/p> Linux下调用程序就要改成下面的格式 Windows下调用系l命?/p> Linux下调用系l命令就要改成下面的格式 Windows下调用系l命令ƈ弹出命o行窗?/p> Linux下调用系l命令ƈ弹出l端H口pҎ下面的格?/p> q有要设|调用程序的工作目录p 下面我们用maven做到自动化! 我们利用maven的生命周期和jetty插g来实现?span style="color: #008000; "> 下面描述下做的自动化web集成试实现的原理?/p> 1Q在生命周期pre-integration-test启动jetty容器 2Q在生命周期integration-test中测试我们写?**IT.javac?/p> 3Q在post-integration-test shutdow jetty容器?/p> 在pom.xml中加入代码如下: 然后可以编写测试用例了 步骤如下Q?/p> 1Q定义一个以此命名的****IT的测试类Qintegration test~写Q, 在里面华丽的写好你的试逻辑?/p> 再此不D例了Q主要一个思\可以用httpclint来实现里面的试代码?br /> 2Q然?执行 mvn clean post-integration-test -Pittest 好了 可以看到我们测试用例是否通过?/p>
1Qnagios install 参考官?nbsp;
上面只是参考,误行安装最新版本的nagiosQƈ自行忽略掉Apache的配|,我们来配|nginx支持nagios?br />
2Q?/span>理解什么是cgi。fastcgi。写的非常棒。要多棒有多?br />
参考文章:http://www.cnblogs.com/skynet/p/4173450.html
3Q上文理解了Q就好办了。我们的目的是让nginx支持执行我们nagios下的cgi。nginxZ安全性等考虑不让直接执行cgiQ但支持fastcgiQ所以我们要用到一个fastcig的warp来封装cgi
autoreconf -i
./configure
make
make instal
如果aotoreconf执行不了Q请自行安装autoreconf?br />
然后是怎么使用fcgiwarp Q作者提C2U用方法(针对q?U方法在nginx配置E微不同Q:
Most probably you will want fcgiwrap be launched by www-servers/spawn-fcgi. Or you could use the author's Perl launcher - see the homepage for that.
W?U是作者自己写的perl 的启动器Q作者说在他?a target="_blank">主页呢。。。。copy一下放在下?br />
use strict;
use warnings FATAL => qw( all );
use IO::Socket::UNIX;
my $bin_path = '/usr/local/bin/fcgiwrap';
my $socket_path = $ARGV[0] || '/tmp/cgi.sock';
my $num_children = $ARGV[1] || 1;
close STDIN;
unlink $socket_path;
my $socket = IO::Socket::UNIX->new(
Local => $socket_path,
Listen => 100,
);
die "Cannot create socket at $socket_path: $!\n" unless $socket;
for (1 .. $num_children) {
my $pid = fork;
die "Cannot fork: $!" unless defined $pid;
next if $pid;
exec $bin_path;
die "Failed to exec $bin_path: $!\n";
}
我们把这个文件保存成 /etc/init.d/fcgiwrap 做成服务执行卛_?br />
W二U方法是用fastcgi?span style="background-color: #ffffff; color: #333333; font-family: 微Y雅黑, sans-serif; font-size: 13px;">q程理器来启动?br />
q是GitHub上开源的牛逼项?/span>spawn-fcgi https://github.com/lighttpd/spawn-fcgi
我们写一个启动脚?nbsp;
最后脓一个nginx单的配置?br />
server_name nagios.tony.com; #自己的域?br /> access_log /var/log/nginx/nagios-access.log;
error_log /var/log/nginx/nagios-error.log; #日志位置Q发现nagios不能在浏览器展示Q请看日志,看日志,
# auth_basic "Private";
# auth_basic_user_file /etc/nagios/htpasswd.users; #把认证先L。跑h在说在?要把 /usr/local/nagios/etc/cgi.cfg 中的use_ssl_authentication=0
root /usr/local/nagios/share; #/usr/local/nagios nagios安装目录
index index.php index.html;
#php 的配|,误行去解决?br /> location ~ \.php$ {
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:9000; #php-fpm
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
location /nagios {
alias /usr/local/nagios/share;
}
location ~ \.cgi$ {
root /usr/local/nagios/sbin;
rewrite ^/nagios/cgi-bin/(.*)\.cgi /$1.cgi break;
include /etc/nginx/fastcgi_params;
fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param SCRIPT_FILENAME /usr/local/nagios/sbin/$fastcgi_script_name;
fastcgi_pass unix:/tmp/cgi.sock; #q是上面W一U方式的配置?br /> #fastcgi_pass 127.0.0.1:9009; #q是上面W二U方式的配置?br /> }
}
]]>
]]>
]]>
]]>
]]> 使用set -u
chroot=$1 ... rm -rf $chroot/usr/share/doc
如果上面的代码你没有l参数就q行Q你不会仅仅删除掉chroot中的文Q而是系l的所有文都删除。那你应该做些什么呢Q好在bash提供?em>set -uQ当你用未初始化的变量Ӟ让bash自动退出。你也可以用可L更Z点的set -o nounset? 使用set -e
E序防M - 考虑意料之外的事
准备好处理文件名中的I格
讄的陷?
信号描述
INT TERM EXIT
竟态条?(wikipedia)
保持原子?
]]>
]]>
Process proc =Runtime.getRuntime().exec(cmd);
Process proc =Runtime.getRuntime().exec(cmd);
Process proc =Runtime.getRuntime().exec(cmd);
Process proc =Runtime.getRuntime().exec(cmd);
同理Q?div>ProcessBuilder也可以这么用管?Q?|
]]>
<profile>
<id>ittest</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>run-integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<contextPath>/</contextPath>
<stopPort>9966</stopPort>
<stopKey>stop-jetty-for-it</stopKey>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>6211</port>
</connector>
</connectors>
</configuration>
<executions>
<execution>
<id>start-it-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-it-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>