linux网页调用shell脚本
昨天调试一个内部监控面板,页调用同事把服务器负载数据写进文件,脚本让我用网页展示。页调用我第一反应是脚本写个Python后端,他摆摆手:“直接调shell脚本不就行了?页调用” 我愣了两秒,然后意识到他说得对——Linux下,脚本网页和shell之间隔着的页调用不过是一层薄薄的CGI协议。
原理说白了就一句话:web服务器收到请求后,脚本把参数丢给shell脚本,页调用脚本把结果吐回给服务器,脚本服务器再包装成HTTP响应。页调用Apache和Nginx都支持这个,脚本但配置方式完全不同。页调用Apache老派但省心,脚本a2enmod cgi开启模块,页调用在目录配置里加上Options +ExecCGI和AddHandler cgi-script .sh,把脚本扔进/usr/lib/cgi-bin/就能跑。Nginx呢,没有原生CGI,得靠fcgiwrap这个瘦子包一层,配置起来像拼乐高——先装fcgiwrap,再写个upstream,最后location里include fastcgi_params。第一次配Nginx的人多半会在权限上卡半小时,我亲眼见过同事把fastcgi_pass unix:/var/run/fcgiwrap.socket;写错成http://开头,页面直接502。
脚本本身没什么魔法。一个最简单的例子:
#!/bin/bash
echo "Content-type: text/html"
echo ""
echo "<h1>$(uptime)</h1>"
第一行告诉系统用bash解释,第二行是HTTP头,空行之后才是正文。就这么朴素。但坑在细节里——脚本必须给执行权限,chmod +x漏了就是403;换行符必须是Unix的LF,Windows传上去的CRLF会让服务器直接抛500错误,这毛病我犯过两次,每次排查都要十分钟。
传参数也有讲究。GET请求的查询字符串会变成环境变量QUERY_STRING,比如访问http://server/cgi-bin/test.sh?name=tom,脚本里用$QUERY_STRING拿到的是整串name=tom,自己解析。POST请求的数据走标准输入,read或者cat都能读。但别指望shell帮你做URL解码——%20不会自动变成空格,name=Tom%26Jerry里的&会被拆成两个变量。真要处理复杂表单,要么装个jq配合curl自己解析,要么干脆别用shell,换个正经语言。
安全性是绕不开的雷区。shell脚本里最怕拼接命令时被注入,比如用户传个; rm -rf /进去。规则只有一条:永远不要直接eval外部输入。用case语句白名单过滤,或者用grep -E校验格式,再不行就printf '%q'转义。我见过最野的写法是把IP地址直接拼进ping命令里,结果有人传了127.0.0.1; cat /etc/passwd,虽然没造成破坏,但日志里那行记录够吓人。
实际用起来,这套东西适合内部工具、快速原型、或者跟cron任务联动。比如我写了个定时备份脚本,每天凌晨跑完把结果追加到日志,网页端再用个shell脚本tail -20那个文件,管理员看一眼就知道昨晚备份成没成。不用装数据库,不用写前端框架,一个页面加三行shell,五分钟搞定。但你要是想搞个用户登录系统,或者要跟Redis交互,趁早换Python或Go——shell的字符串处理能力撑不住那种复杂度,硬撑只会让代码变成意大利面条。
最后说个冷知识:#!/bin/bash和#!/bin/sh在Ubuntu上可不是一回事。/bin/sh指向dash,语法更严格,$((i++))这种写法在dash里直接报错。所以脚本头部写清楚解释器,别图省事写个sh就完事。血的教训,别问我怎么知道的。
