首页 > 解决方案 > 远程运行与本地运行时,Bash while 循环的行为不同

问题描述

有一个简单的日志文件 /tmp/test.log

2019-12-09 15:28:12 UTC [6308] App1
2019-12-09 15:29:08 UTC [1246] App2
2019-12-09 15:30:57 UTC [5541] App3

有一个简单的 bash 脚本 /tmp/test.sh

#!/bin/bash
# 
while read line
do
    echo $line
done < /tmp/test.log

如果我在本地运行此脚本,它将按预期工作:

$ /tmp/test.sh
2019-12-09 15:28:12 UTC [6308] App1
2019-12-09 15:29:08 UTC [1246] App2
2019-12-09 15:30:57 UTC [5541] App3

但是如果我通过 ssh 运行这个脚本,我会得到一些意想不到的东西:

$ ssh 127.0.0.1 "/tmp/test.sh"
2019-12-09 15:28:12 UTC 0 3 App1
2019-12-09 15:29:08 UTC 1 App2
2019-12-09 15:30:57 UTC 1 App3

有人知道为什么吗?

标签: bashfilessh

解决方案


我认为这里有几个不同的东西相互作用导致了令人困惑的结果。这是我最好的猜测解释。

您对重要 IFS 变量的值可能是 20 09 0a(这些是十六进制代码,用于空格、制表符、换行符)。这对echo(以及read陈述)有重要影响。(用于echo -n "$IFS" | hexdump查看值)。

read命令将从文件中读取每一行,并将它们全部添加到 line变量中。它实际上会对每一行进行标记,并将每个单词添加到 line.

echo语句将导致对 中的值进行评估line。shell 将使用IFS将值分隔为单独的标记,然后将它们全部传递给 echo,echo 将打印它们,用空格分隔。即,有一个很难观察到的外壳预处理步骤。

像 etc 这样的标记会出现问题[6308]。方括号用于 shell globbing,因此当 bash 遇到类似的东西时,[6308]它会尝试将其与当前目录中名为 like 6or 3or 0or 的文件匹配8。因此,如果您有两个名为0and的文件3,它将同时匹配它们并返回字符串0 3

在本地运行而不是通过 ssh 运行时得到不同结果的原因可能只是由于调用脚本的当前工作目录。通过 ssh 运行时,它不会从您的本地当前目录运行。要查找用于 ssh 调用的 cwd,只需pwd在文件顶部附近放置一个。我猜测通过 ssh 调用使用的目录具有名为0etc的文件。

简单的解决方法是防止 shell 处理 的内容$line,方法是用引号将其括起来,如 echo "$line". 您也可以调整 IFS,但引用字符串是通常的做法。


推荐阅读