AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 771105
Accepted
mesr
mesr
Asked: 2024-02-26 11:50:07 +0800 CST2024-02-26 11:50:07 +0800 CST 2024-02-26 11:50:07 +0800 CST

执行和获取 Bash 脚本之间出现奇怪的不一致

  • 772

我知道这不是一个描述性很强的标题(欢迎提出建议),但事实是我已经为此烦恼了好几个小时,而且我不知道问题的根源可能在哪里。

我为本地网络上的对等点之间的 CLI 聊天编写了一个简单的 Bash 脚本:

#!/usr/bin/env bash

# Usage: ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>

# set -x
set -o errexit -o nounset -o pipefail

IFS=':' read -a socket <<< "$1"
LOCAL_IP=${socket[0]}
LOCAL_PORT=${socket[1]}

IFS=':' read -a socket <<< "$2"
REMOTE_IP=${socket[0]}
REMOTE_PORT=${socket[1]}

RECV_FIFO=".tmp.lanchat"

trap "rm '$RECV_FIFO'; kill 0" EXIT

mkfifo "$RECV_FIFO"

while true; do nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" > "$RECV_FIFO"; done &

TMUX_TOP="while true; do cat '$RECV_FIFO'; done"
TMUX_BOTTOM="while IFS= read -r line; do nc -n -q 0 '$REMOTE_IP' '$REMOTE_PORT' <<< \$line; done"

tmux new "$TMUX_TOP" \; split -v "$TMUX_BOTTOM"

IP 172.16.0.2 上的机器是运行 Debian 11 的 VPS,172.16.0.100 上是我运行 Arch 的本地计算机。

当我在两侧的提示符下手动运行命令时,我得到了想要的结果,这证实了网络通信没有问题,并且脚本的逻辑是正确的。

## VPS (Debian) side as follows; exchange IPs for local (Arch) side.
$ mkfifo .tmp.lanchat
$ while true; do nc -n -l -q 0 -s 172.16.0.2 -p 1234 > .tmp.lanchat; done &
$ tmux new "while true; do cat .tmp.lanchat; done" \; split -v "while IFS= read -r line; do nc -n -q 0 172.16.0.100 1234 <<< \$line; done"
## Test communication in both directions: all right; then CTRL-C twice to exit both tmux panels
$ kill %1; rm .tmp.lanchat

然而,当我将双方作为脚本运行时,只有本地端(Arch)打印来自服务器(Debian)的消息。服务器没有从我的本地计算机打印任何内容。当我使用 跟踪执行时set -x,两侧的所有内容看起来都与我手动输入的命令完全相同,用正确的值代替变量。

现在奇怪的是,如果我在 Arch 端运行脚本并在 Debian 端按照提示符(如上所示)运行命令,那么一切都会再次正常工作。此外,如果我在 Arch 端执行脚本但在 Debian 端获取它,那也可以正常工作。

向 Arch 端的两个nc调用添加详细输出,甚至打印Connection to 172.16.0.2 1234 port [tcp/*] succeeded!。但是,在 Debian 端以监听模式添加 atee log.txt到nc调用中不会捕获任何内容:

#...
while true; do
    nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" | tee log.txt > "$RECV_FIFO";
done &
#...

我尝试在两个对等点之间以所有可能的顺序建立连接。我什至重新启动了服务器和本地计算机,以确保不存在以某种方式逃避检测的nc拥抱套接字的孤立或僵尸实例。

现在,Debian 和 Arch 运行不同版本的nc。所以,从表面上看,这听起来可能是一个可能的解释。但是,在 Debian 端获取脚本运行良好的事实是否排除了这种可能性?

这里到底发生了什么事?

bash
  • 1 1 个回答
  • 93 Views

1 个回答

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2024-02-26T19:51:10+08:002024-02-26T19:51:10+08:00

    我已经在 Debian 12 中测试了您的脚本(本地主机到本地主机,单独的工作目录),并确认了问题。我的nc来自netcat-traditional 1.10-47(即不是来自netcat-openbsd)。

    问题出在-q 0听力上nc。从man 1 nc:

    -q seconds
    在 stdin 上的 EOF 后,等待指定的秒数,然后退出。如果秒为负数,则永远等待。

    似乎监听nc在退出之前等待传入连接-q 0,但它不等待传入数据。建立连接和传输数据是单独的事件,因为该-q 0工具通常会在中间退出。这是一场比赛;在我的测试中,监听nc 有时会将传入的数据中继到管道。

    触发意外行为的 EOF 会立即发生,因为当没有作业控制的 shell 运行异步命令(以 终止&,这就是您使用监听运行循环的方式nc)时,它必须将其标准输入重定向到/dev/null或重定向到等效文件。

    当您获取脚本时,交互式 shell 会解释它。它可能是启用了作业控制的 bash(交互式 bash 的默认行为)。如果是这样,它会在单独的进程组中运行后台循环,但其标准输入仍然连接到终端(通常这允许我们进行fg后台作业并键入它)。对于后台作业,无法从终端窃取来自 SIGTTIN 的输入,EOF 永远不会发生。这样,当脚本被获取时,监听nc就不会受到-q 0在没有获取的情况下运行脚本时出现的问题。

    指定-q 1聆听nc将在实践中有所帮助(虽然理论上仍然很活跃,我猜),但我认为最好使用-q -1(永远等待)或简单地省略-q(在我的测试中,默认行为似乎是“永远等待”)。

    -q 0因为连接nc(tmux 内的连接)是有意义的,您确实希望nc在发送有效负载后立即退出。

    nc您的 Arch 上的行为有所不同,可能是因为它不同,或者可能是因为当时操作系统的整体压力影响了比赛。

    教训是:如果nc+对nc -l仅在一个方向发送数据(每行使用一个这样的对),那么-q 0对于发送方来说是一个有用的选项;但对于接收者来说这是不必要的,在某些情况下甚至是有害的。


    请注意,这个答案解决了明确的问题,仅此而已。例如:

    • 存在代码注入漏洞(./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>"'; rogue command'");
    • nc当一端或另一端没有监听时,存在很短的时间窗口;
    • 一对ncs 可能足以处理整个“会话”。

    答案没有解决这些问题,但我可以给你一个替代脚本的草图:

    #!/usr/bin/env bash
    
    target="$(tmux new -dP 'tail -f /dev/null')"
    uptty="$(tmux display-message -p -F '#{pane_tty}' -t "$target")"
    tmux split -t "$target" -v "
       rlwrap tee    >(sed -u 's/^/      < /' | ts %H:%M >${uptty@Q}) \
       | nc ${*@Q} > >(sed -u 's/^/> /'       | ts %H:%M >${uptty@Q})
    "
    tmux a -t "$target"
    

    该脚本确实需要 bash(对于其本身和 tmux 内部)。您可以使用要提供给的参数来运行它nc,例如

    • 首先是聆听方:./lanchat -n -l -s 192.168.11.22 -p 2345,
    • 然后是连接边:./lanchat 192.168.11.22 2345。

    单个nc连接nc处理双向的所有通信。该脚本用于时间戳(如果需要,ts您可以删除两个实例)和使用 readline 进行行编辑(如果需要,您可以删除)。不便于携带;没有会导致缓冲问题,除非你也摆脱了。| ts %H:%M rlwraprlwrap sed -used-uts

    在 bash 5.2.15、tmux 3.3a 中测试。

    • 5

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve