如何在每个请求上运行一个shell脚本?

88
我想每当我的nginx服务器接收到任何HTTP请求时运行一个shell脚本。有什么简单的方法可以做到这一点吗?

你试图用这个解决什么实际的问题?按照你的解释,这可能会导致更多的问题。 - Burhan Khalid
我正在使用树莓派,我希望当我的服务器收到请求时,其中一个LED灯会闪烁。 - Saswat Padhi
你使用的后端语言/框架是什么?大多数后端语言应该允许您调用一个shell脚本。 - Chirag Bhatia - chirag64
6
我的问题是我是否可以在nginx内对其进行修改。我不想手动从PHP或任何其他语言中调用shell脚本,也不想在每个请求中运行它。 - Saswat Padhi
不要忘记将您选择的答案标记为已接受 ;) - Stephan
6个回答

83

您可以通过在nginx.conf文件中的Lua代码执行shell脚本来实现这一功能。您需要安装HttpLuaModule才能够这样做。

以下是一个示例。

location /my-website {
  content_by_lua_block {
    os.execute("/bin/myShellScript.sh")
  } 
}

2
这似乎产生了一个nginx错误... [alert] 6807#0: waitpid() failed (10: No child processes) - 请参阅http://serverfault.com/q/432609/86531 - Nick
1
似乎os.execute()的标准错误会进入nginx错误日志,而标准输出则被丢弃。os.execute()函数返回子进程退出码,因此该示例将输出一个整数。基于io.popen()的某些内容可能更适合传递子进程的标准输出。 - Arto Bendiken
还有一个 https://github.com/juce/lua-resty-shell,但它需要外部守护进程。 - Arto Bendiken
1
使用content_by_lua被标记为不推荐使用,因此我已经相应地更新了您的答案。 - Arnaud P
3
你能否获取请求中的POST或GET参数以传递给脚本? - Chris
尝试安装openresty来实现这一点,但安装后nginx无法启动。不得不移除openresty才能重新启动nginx。 - dmirg

58

我在这个地址上线上找到以下信息:https://www.ruby-forum.com/topic/2960191

这需要您在机器上安装fcgiwrap。 它真的很简单:

sudo apt-get install fcgiwrap

示例脚本(必须可执行)

#!/bin/sh
# -*- coding: utf-8 -*-
NAME=`"cpuinfo"`
echo "Content-type:text/html\r\n"
echo "<html><head>"
echo "<title>$NAME</title>"
echo '<meta name="description" content="'$NAME'">'
echo '<meta name="keywords" content="'$NAME'">'
echo '<meta http-equiv="Content-type"
content="text/html;charset=UTF-8">'
echo '<meta name="ROBOTS" content="noindex">'
echo "</head><body><pre>"
date
echo "\nuname -a"
uname -a
echo "\ncpuinfo"
cat /proc/cpuinfo
echo "</pre></body></html>"

还可以将其作为包含文件使用,不仅限于 shell 脚本。

location ~ (\.cgi|\.py|\.sh|\.pl|\.lua)$ {
    gzip off;
    root /var/www/$server_name;
    autoindex on;
    fastcgi_pass unix:/var/run/fcgiwrap.socket;
    include /etc/nginx/fastcgi_params;
    fastcgi_param DOCUMENT_ROOT /var/www/$server_name;
    fastcgi_param SCRIPT_FILENAME /var/www/$server_name$fastcgi_script_name;
}

我发现它非常有用,希望它能帮助您完成您的树莓派项目。


1
请参阅 https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/ 和 https://www.nginx.com/resources/wiki/start/topics/examples/fastcgiexample/。 - Justin M. Keyes
我需要在shell脚本上设置正确的权限 sudo chmod 755 /var/www/www.example.com/cpu.sh(在此处找到 https://www.howtoforge.com/serving-cgi-scripts-with-nginx-on-centos-6.0-p2) - adriaan
我发现这篇文章对这种方法也非常有帮助:https://sleeplessbeastie.eu/2017/09/18/how-to-execute-cgi-scripts-using-fcgiwrap/ - fei0x

3
如果你更喜欢在Python中拥有完全的控制权:
  • 创建/opt/httpbot.py文件:
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import subprocess

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self._handle()

    def do_POST(self):
        self._handle()

    def _handle(self):
        try:
            self.log_message("command: %s", self.path)
            if self.path == '/foo':
                subprocess.run(
                    "cd /opt/bar && GIT_SSH_COMMAND='ssh -i .ssh/id_rsa' git pull",
                    shell=True,
                )
        finally:
            self.send_response(200)
            self.send_header("content-type", "application/json")
            self.end_headers()
            self.wfile.write('{"ok": true}\r\n'.encode())

if __name__ == "__main__":
    HTTPServer(("127.0.0.1", 4242), Handler).serve_forever()
  • 这里没有并发/并行,所以 httpbot 一次只运行一个命令,不会产生冲突。
  • 运行apt install supervisor
  • 创建/etc/supervisor/conf.d/httpbot.conf
[program:httpbot]
environment=PYTHONUNBUFFERED="TRUE"
directory=/opt
command=/opt/httpbot.py
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/httpbot.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=10
  • 将以下内容添加到您的nginx服务器中:
    location /foo {
        proxy_pass http://127.0.0.1:4242/foo;
    }
  • 运行:
chmod u+x /opt/httpbot.py

service supervisor status
# If stopped:
service supervisor start

supervisorctl status
# If httpbot is not running:
supervisorctl update

curl https://example.com/foo
# Should return {"ok": true}

tail /var/log/httpbot.log
# Should show `command: /foo` and the output of shell script

2
  1. 安装 OpenResty (OpenResty 是通过附加模块增强了 Nginx 的版本)。请参阅 https://openresty.org/en/getting-started.html
  2. 在实例上配置 AWS CLI。
  3. 编写一个 shell 脚本,从指定的 S3 存储桶下载文件。
  4. 在 nginx.conf 文件中进行所需的更改。
  5. 重启 nginx 服务器。

我已经使用 curl 测试了 HTTP 请求,并在相应实例的 /tmp 目录中下载了该文件。

curl -I http://localhost:8080/

输出:

curl -I http://localhost:8080/
HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Date: Tue, 14 Aug 2018 07:34:49 GMT
Content-Type: text/plain
Connection: keep-alive 

nginx.conf文件内容:

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
           default_type text/html;
           content_by_lua '
                ngx.say("<p>hello, world</p>")
           ';
        }

        location / {
            content_by_lua_block{
            os.execute("sh /tmp/s3.sh")
            }
        }

    }
}

我没有看到任何Shell脚本输出,并且它说我的文件/tmp/s3.sh不存在,即使我已经创建了该文件并且在直接执行时可以运行。 - Prashanth
我的 os.execute 写入到标准输出(在 nginx 日志中可以看到)但是浏览器中却得到了空白页面。 - Dzenly
ngx.say 正常情况下会将内容写入响应中。 - Dzenly

1

您可以使用Nginx的Perl模块,该模块通常作为一个库的一部分,并且可以轻松安装。以下是调用系统curl命令的示例:

   location /mint {
       perl '
            sub {
               my $r = shift;
               $r->send_http_header("text/html");
               $r->print(`curl -X POST --data \'{"method":"evm_mine"}\' localhost:7545`);
               return OK;
            }
       '; 
}

0

您还可以使用nginx镜像模块,并将其代理传递给运行任何内容的Web脚本,在我的情况下,我只是将其添加到我的主站点位置{...}

mirror /mirror;
mirror_request_body off;

然后我运行了一个执行任何操作的PHP脚本,创建了一个名为“mirror”的新位置...

location = /mirror {
    internal;
    proxy_pass http://localhost/run_script.php;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
}

https://nginx.org/en/docs/http/ngx_http_mirror_module.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接