在远程主机(通过 Cron 的 SSH)上捕获的命令输出为空白
Captured output of command on remote host (SSH via Cron) is blank
下面是一个脚本,它通过 SSH 登录远程主机(Cisco IOS-XR 路由器)和 运行s 一个命令。这个想法是获取命令的结果(一个整数),以便它可以被 Cacti 绘制出来。 Cacti 运行每 5 分钟执行一次此脚本,当它 运行 是正常的轮询例程时:
#!/bin/bash
if [[ -z ]]
then
exit 1
fi
HOST=""
USER="cact-ssh-user"
TIMEOUT=10s
export SSHPASS="aaaaaaaaaaaaa"
CMD="show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries"
RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" 2>/dev/null)
GRT_UCASTV4_USED=$(echo -n "$RAW_OUTPUT" | grep "Entries" | awk '{print }' | tr -d "," | tr -d " ")
echo -n "ucastv4_used:$GRT_UCASTV4_USED"
这个命令通过交互式 shell 工作正常(当我 运行 Cacti 服务器上的脚本使用 /path/to/script/script.sh 10.0.0.1
时。但是当 Cacti cronjob 运行输出只是空白。所以在我与 Cacti 服务器的 SSH 会话中,输出是:
$ ./script 10.0.0.1
ucastv4_used:1234
在 Cacti 日志中,输出是:05/22/2017 03:35:21 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:
我已经 su
发给了 Cacti 用户,脚本工作正常。所以这似乎是特定于它的 运行nings 作为一个 cronjob,SSH 命令的输出被神奇地重定向到某个地方,我不知道在哪里或为什么。
为了尝试调试它,我在脚本中添加了以下行(直接在 #!/bin/bash
下)并等待 Cacti 5 分钟的轮询间隔到 运行(我可以在 Cacti 中看到每 5 分钟调用一次脚本时记录);
exec >/tmp/stdout.log 2>/tmp/stderr.log
set -x
stdout.log
仅包含与 cacti.log
相同的 ucastv4_used:
,stderr.log
文件包含远程 SSH 主机的登录标志,仅此而已。 SSH 输出到哪里去了?
我厌倦了将脚本中的 SSH 行更改为输出到文件,然后从那里读取:
timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" > /tmp/output 2>/dev/null
GRT_UCASTV4_USED=$(grep "Entries" /tmp/output | awk '{print }' | tr -d "," | tr -d " ")
文件 /tmp/output
为空,因此 GRT_UCASTV4_USED
变量也为空。 stdout.log
最终和以前一样:ucastv4_used:
我还尝试将 #!/bin/bash
更改为 #!/bin/bash -i
以强制进行交互式会话。如果我将 echo $PS1
添加到脚本中,我可以在 stdout.log
文件中看到设置了 $PS1
而没有设置 -i
什么都不打印。但是,SSH 命令仍然没有输出。 SSH输出的命令去哪里了?
我也尝试过使用 ssh ..... | tee /tmp/output
以便输出显示在 /tmp/output
和 /tmp/stdout.log
中,但两者都是空白的。
我可以在远程路由器上看到 SSH 会话正在进入并且 运行正在执行命令。这是来自 debug ssh server
:
RP/0/RSP0/CPU0:May 22 14:52:57.976 UTC: SSHD_[65909]: (open_master_file) command added show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries
此外,由于这是通过我与 Cacti 服务器的交互式会话进行的,我猜测问题出在此处而不是路由器。我也相信 Cacti 本身不是问题,我可以触发 spine 从我的交互式 SSH 会话中轮询该路由器主机并且脚本工作正常(进一步指出在非交互式 shell SSH 输出是 evaporating):
$ cd /usr/local/spine/bin
$ ./spine -V 7 69 69
...
05/22/2017 04:06:56 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:658809
所以似乎 SSH 输出被重定向到某个地方,我不能 "get it" 或者路由器以某种方式知道这是一个非交互式 SSH 客户端并且没有发送回任何东西。我还能如何调试它?
更新 1
在 Cisco 路由器上使用 debug ssh server
我捕获了调试日志,当我通过我的交互式 SSH 会话将脚本 运行 连接到 Cacti 服务器时,当它通过 Cacti 的轮询 运行 时 interval/cron 工作。我已经 diff
编辑了输出,我能找到的唯一有趣的外观差异(除了 SSH PID 更改和 Cacti 服务器的临时源端口更改等)如下:
*** 132,145 ****
(sshd_interactive_shell) *** removing alarm
sshd_interactive_shell - ptyfd = 46
event_contex_init done
! sshd_ptytonet - Channel 1 Received EOT (bytes:1)
! sshd_ptytonet - Channel 1 exec command executed sending CHANNEL_CLOSE
! (close_channel), pid:182260085, sig rcvd:1, state:10 chan_id:1
! addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182260085
! Cleanup sshd process 182260085, session id 1, channel_id 1
! addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182260085
close_channel: Accounting stopped: scriptaccount
! In delete channel code, pid:182260085, sig rcvd:1, state:10 chan_id:1
Sending Exit Status: 0 sig: 1
Sending Channel EOF msg
Sending Channel close msg for remote_chan_id = 0 chan_id = 1
--- 134,147 ----
(sshd_interactive_shell) *** removing alarm
sshd_interactive_shell - ptyfd = 46
event_contex_init done
! Pad_len = 6, Packlen = 12
! sshd_nettopty: EOF received. Disconnecting session
! (close_channel), pid:182329717, sig rcvd:1, state:10 chan_id:1
! addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182329717
! Cleanup sshd process 182329717, session id 1, channel_id 1
! addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182329717
close_channel: Accounting stopped: scriptaccount
! In delete channel code, pid:182329717, sig rcvd:1, state:10 chan_id:1
Sending Exit Status: 0 sig: 1
Sending Channel EOF msg
Sending Channel close msg for remote_chan_id = 0 chan_id = 1
上半部分是我与 Cacti 服务器的交互会话。我在 top hald sshd_ptytonet - Channel 1 Received EOT (bytes:1)
中注意到,而通过 cronjob,调试显示 sshd_nettopty: EOF received. Disconnecting session
。非交互式会话是否只是将我的 SSH 命令传递给远程主机并尽快退出(因此它不会等待 SSH 服务器响应命令输出)?
- 首先,告诉 SSH 客户端不要分配带有 -T 选项的 PTY,因为显然 cron 没有。
- 然后在 stdin 上给它一些无限的东西,所以它将保持 运行 直到 stdout
是开放的,我们有 /dev/zero 正是为此目的。
RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -T -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" </dev/zero 2>/dev/null)
下面是一个脚本,它通过 SSH 登录远程主机(Cisco IOS-XR 路由器)和 运行s 一个命令。这个想法是获取命令的结果(一个整数),以便它可以被 Cacti 绘制出来。 Cacti 运行每 5 分钟执行一次此脚本,当它 运行 是正常的轮询例程时:
#!/bin/bash
if [[ -z ]]
then
exit 1
fi
HOST=""
USER="cact-ssh-user"
TIMEOUT=10s
export SSHPASS="aaaaaaaaaaaaa"
CMD="show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries"
RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" 2>/dev/null)
GRT_UCASTV4_USED=$(echo -n "$RAW_OUTPUT" | grep "Entries" | awk '{print }' | tr -d "," | tr -d " ")
echo -n "ucastv4_used:$GRT_UCASTV4_USED"
这个命令通过交互式 shell 工作正常(当我 运行 Cacti 服务器上的脚本使用 /path/to/script/script.sh 10.0.0.1
时。但是当 Cacti cronjob 运行输出只是空白。所以在我与 Cacti 服务器的 SSH 会话中,输出是:
$ ./script 10.0.0.1
ucastv4_used:1234
在 Cacti 日志中,输出是:05/22/2017 03:35:21 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:
我已经 su
发给了 Cacti 用户,脚本工作正常。所以这似乎是特定于它的 运行nings 作为一个 cronjob,SSH 命令的输出被神奇地重定向到某个地方,我不知道在哪里或为什么。
为了尝试调试它,我在脚本中添加了以下行(直接在 #!/bin/bash
下)并等待 Cacti 5 分钟的轮询间隔到 运行(我可以在 Cacti 中看到每 5 分钟调用一次脚本时记录);
exec >/tmp/stdout.log 2>/tmp/stderr.log
set -x
stdout.log
仅包含与 cacti.log
相同的 ucastv4_used:
,stderr.log
文件包含远程 SSH 主机的登录标志,仅此而已。 SSH 输出到哪里去了?
我厌倦了将脚本中的 SSH 行更改为输出到文件,然后从那里读取:
timeout $TIMEOUT sshpass -e ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" > /tmp/output 2>/dev/null
GRT_UCASTV4_USED=$(grep "Entries" /tmp/output | awk '{print }' | tr -d "," | tr -d " ")
文件 /tmp/output
为空,因此 GRT_UCASTV4_USED
变量也为空。 stdout.log
最终和以前一样:ucastv4_used:
我还尝试将 #!/bin/bash
更改为 #!/bin/bash -i
以强制进行交互式会话。如果我将 echo $PS1
添加到脚本中,我可以在 stdout.log
文件中看到设置了 $PS1
而没有设置 -i
什么都不打印。但是,SSH 命令仍然没有输出。 SSH输出的命令去哪里了?
我也尝试过使用 ssh ..... | tee /tmp/output
以便输出显示在 /tmp/output
和 /tmp/stdout.log
中,但两者都是空白的。
我可以在远程路由器上看到 SSH 会话正在进入并且 运行正在执行命令。这是来自 debug ssh server
:
RP/0/RSP0/CPU0:May 22 14:52:57.976 UTC: SSHD_[65909]: (open_master_file) command added show controllers np struct IPV4-LEAF-FAST-P np0 | in Entries
此外,由于这是通过我与 Cacti 服务器的交互式会话进行的,我猜测问题出在此处而不是路由器。我也相信 Cacti 本身不是问题,我可以触发 spine 从我的交互式 SSH 会话中轮询该路由器主机并且脚本工作正常(进一步指出在非交互式 shell SSH 输出是 evaporating):
$ cd /usr/local/spine/bin
$ ./spine -V 7 69 69
...
05/22/2017 04:06:56 PM - SPINE: Poller[0] Host[69] TH[1] DS[6837] SCRIPT: /opt/scripts/cacti-scripts/asr9001-get-tcam-ucast-usage.sh 10.0.0.1, output: ucastv4_used:658809
所以似乎 SSH 输出被重定向到某个地方,我不能 "get it" 或者路由器以某种方式知道这是一个非交互式 SSH 客户端并且没有发送回任何东西。我还能如何调试它?
更新 1
在 Cisco 路由器上使用 debug ssh server
我捕获了调试日志,当我通过我的交互式 SSH 会话将脚本 运行 连接到 Cacti 服务器时,当它通过 Cacti 的轮询 运行 时 interval/cron 工作。我已经 diff
编辑了输出,我能找到的唯一有趣的外观差异(除了 SSH PID 更改和 Cacti 服务器的临时源端口更改等)如下:
*** 132,145 ****
(sshd_interactive_shell) *** removing alarm
sshd_interactive_shell - ptyfd = 46
event_contex_init done
! sshd_ptytonet - Channel 1 Received EOT (bytes:1)
! sshd_ptytonet - Channel 1 exec command executed sending CHANNEL_CLOSE
! (close_channel), pid:182260085, sig rcvd:1, state:10 chan_id:1
! addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182260085
! Cleanup sshd process 182260085, session id 1, channel_id 1
! addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182260085
close_channel: Accounting stopped: scriptaccount
! In delete channel code, pid:182260085, sig rcvd:1, state:10 chan_id:1
Sending Exit Status: 0 sig: 1
Sending Channel EOF msg
Sending Channel close msg for remote_chan_id = 0 chan_id = 1
--- 134,147 ----
(sshd_interactive_shell) *** removing alarm
sshd_interactive_shell - ptyfd = 46
event_contex_init done
! Pad_len = 6, Packlen = 12
! sshd_nettopty: EOF received. Disconnecting session
! (close_channel), pid:182329717, sig rcvd:1, state:10 chan_id:1
! addrem_ssh_info_tuple: REMOVE Inside the critical Section %pid:182329717
! Cleanup sshd process 182329717, session id 1, channel_id 1
! addrem_ssh_info_tuple: REMOVE exiting the Critical Section %pid:182329717
close_channel: Accounting stopped: scriptaccount
! In delete channel code, pid:182329717, sig rcvd:1, state:10 chan_id:1
Sending Exit Status: 0 sig: 1
Sending Channel EOF msg
Sending Channel close msg for remote_chan_id = 0 chan_id = 1
上半部分是我与 Cacti 服务器的交互会话。我在 top hald sshd_ptytonet - Channel 1 Received EOT (bytes:1)
中注意到,而通过 cronjob,调试显示 sshd_nettopty: EOF received. Disconnecting session
。非交互式会话是否只是将我的 SSH 命令传递给远程主机并尽快退出(因此它不会等待 SSH 服务器响应命令输出)?
- 首先,告诉 SSH 客户端不要分配带有 -T 选项的 PTY,因为显然 cron 没有。
- 然后在 stdin 上给它一些无限的东西,所以它将保持 运行 直到 stdout 是开放的,我们有 /dev/zero 正是为此目的。
RAW_OUTPUT=$(timeout $TIMEOUT sshpass -e ssh -T -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null $USER@$HOST "$CMD" </dev/zero 2>/dev/null)