“读取”命令未在“读取行”循环中执行[重复]

Posted

技术标签:

【中文标题】“读取”命令未在“读取行”循环中执行[重复]【英文标题】:"read" command not executing in "while read line" loop [duplicate] 【发布时间】:2017-05-12 22:49:18 【问题描述】:

第一次发帖!我真的需要帮助,我在谷歌上查看了这个问题,但无法找到对我有用的答案。所以这就是问题所在。 我在 bash 中编写类似框架的代码很有趣。每个人都可以创建自己的模块并将其添加到框架中。但。为了知道脚本需要什么参数,我创建了一个必须在每个模块中的“args.conf”文件,看起来像这样:

LHOST;true;The IP the remote payload will connect to.
LPORT;true;The port the remote payload will connect to.

第一列是参数名称,第二列定义是否需要,第三列是描述。无论如何,长话短说,框架应该逐行读取 args.conf 文件,以向用户询问每个参数的值。这是一段代码:

info "Reading module $name argument list..."
while read line; do
    echo $line > line.tmp
    arg=`cut -d ";" -f 1 line.tmp`
    requ=`cut -d ";" -f 2 line.tmp`
    if [ $requ = "true" ]; then
        echo "[This argument is required]"
    else
        echo "[This argument isn't required, leave a blank space if you don't wan't to use it]"
    fi
    read -p " $arg=" answer
    echo $answer >> arglist.tmp
done < modules/$name/args.conf
tr '\n' ' ' < arglist.tmp > argline.tmp
argline=`cat argline.tmp`
info "Launching module $name..."
cd modules/$name
$interpreter $file $argline
cd ../..
rm arglist.tmp
rm argline.tmp
rm line.tmp
succes "Module $name execution completed."

如您所见,它应该向用户询问每个参数的值...但是:

1) 读取命令似乎没有执行。它只是跳过它,参数没有价值

2) 尽管 args.conf 文件包含 3 行,但循环似乎只执行了一次。我在屏幕上看到的只是一次“[此参数是必需的]”,并且模块刚刚启动(并且由于它没有所需的参数而崩溃......)。

真的不知道怎么办了,这里...希望有人在这里解答^^'。 提前致谢!

(对于最终的错误,我很抱歉,我是法国人)

阿尔法。

【问题讨论】:

您的while read lineread -p " $arg" answer 正在争夺相同的数据。您是否尝试从用户那里阅读?最简单的破解方法是read -p " $arg=" answer &lt; /dev/tty 请参阅the linked question 以获取良好的minimal reproducible example 示例——通过具体和直接关注手头的问题,用四行而不是 20 行来提出相同的问题,删除所有不相关的代码。 【参考方案1】:

正如@that other guy 在评论中指出的那样,问题在于循环中的所有read 命令都是从 args.conf 文件中读取的,而不是用户。我处理这个问题的方法是将conf文件重定向到与stdin(fd #0)不同的文件描述符上;我喜欢为此使用 fd #3:

while read -u3 line; do
    ...
done 3< modules/$name/args.conf

(注意:如果您的 shell 的 read 命令不理解 -u 选项,请改用 read line &lt;&amp;3。)

我建议反对此脚本中的许多其他内容:

变量引用不带双引号,例如echo $line 代替 echo "$line"&lt; modules/$name/args.conf 代替 &lt; "modules/$name/args.conf"。未加引号的变量引用被拆分为单词(如果它们包含空格),并且恰好匹配文件名的任何通配符将被匹配文件列表替换。这可能会导致真的奇怪和间歇性的错误。不幸的是,您对$argline 的使用依赖于分词来分隔多个参数;如果您使用的是bash(不是通用的 POSIX shell),您可以使用数组来代替;我会解决的。

您在各处都使用相对文件路径,并且在脚本中使用cding。这往往是脆弱和令人困惑的,因为脚本中不同位置的文件路径不同,并且用户传入的任何相对路径在脚本cds 在其他地方第一次时将变得无效。更糟糕的是,当您 cd 时,您并没有检查错误,因此如果任何 cd 由于任何原因失败,那么整个脚本的其余部分将在错误的位置运行并奇怪地失败。你最好弄清楚系统的根目录在哪里(作为绝对路径),然后引用其中的所有内容(例如&lt; "$module_root/modules/$name/args.conf")。

实际上,您并没有在任何地方检查错误。在编写任何类型的程序时,尝试思考可能出错的地方以及程序应该如何响应通常是一个好主意(同时也期望你没有想到的事情也会出错)。如果任何简单的命令失败,有些人喜欢使用set -e 让他们的脚本退出,但是this doesn't always do what you'd expect。我更喜欢在我的脚本中显式测试命令的退出状态,例如:

command1 || 
    echo 'command1 failed!' >&2
    exit 1

if command2; then
    echo 'command2 succeeded!' >&2
else
    echo 'command2 failed!' >&2
    exit 1
fi

您正在当前目录中创建临时文件,这可能会产生随机冲突(同时与其他脚本运行、碰巧具有您正在使用的名称的任何文件等)。最好在开始时创建一个临时目录,然后将所有内容存储在其中(同样,通过绝对路径):

module_tmp="$(mktemp -dt module-system)" || 
    echo "Error creating temp directory" >&2
    exit 1

...
echo "$answer" >> "$module_tmp/arglist.tmp"

(顺便说一句,请注意我使用的是$() 而不是反引号。它们更易于阅读,并且没有反引号所具有的一些微妙的语法怪异。我建议切换。)

说到这个,你过度使用临时文件;你正在做的很多事情都可以用 shell 变量和内置的 shell 特性来完成。例如,与其从配置文件中读取行,然后将它们存储在临时文件中并使用cut 将它们拆分为字段,您可以简单地将echo 转换为cut

arg="$(echo "$line" | cut -d ";" -f 1)"

...或者更好的是,使用read 的内置功能根据IFS 的设置来拆分字段:

while IFS=";" read -u3 arg requ description; do

(请注意,由于分配给IFSread 命令的前缀,它只影响那个命令;全局更改IFS 会产生奇怪的效果,应尽可能避免。)

类似地,将参数列表存储在文件中,将换行符转换为空格到另一个文件中,然后读取该文件……您可以跳过任何或所有这些步骤。如果您使用bash,请将 arg 列表存储在数组中:

arglist=()
while ...
    arglist+=("$answer") # or ("#arg=$answer")? Not sure of your syntax.
done ...

"$module_root/modules/$name/$interpreter" "$file" "$arglist[@]"

(这种混乱的语法,带有双引号、花括号、方括号和 at 符号,通常是在 bash 中扩展数组的正确方法。

如果你不能指望bash 像数组这样的扩展,你至少可以用一个普通的变量来做旧的混乱方式:

arglist=""
while ...
    arglist="$arglist $answer" # or "$arglist $arg=$answer"? Not sure of your syntax.
done ...

"$module_root/modules/$name/$interpreter" "$file" $arglist

...但这存在参数被分词和/或扩展为文件列表的风险。

【讨论】:

感谢您的回答!是的,我的编码总是很乱,我不太擅长组织这样的事情^^'。我会尽量记住你的建议^^。顺便说一句,我知道那个 arg="$(echo "$line" | cut -d ";" -f 1)" 东西,我通常总是使用它,但我不知道为什么,当我在这个一段代码,上面写着“找不到命令剪切”......我必须按照我的方式使用它,否则它不起作用......无论如何,谢谢你们的回答,现在就试试!

以上是关于“读取”命令未在“读取行”循环中执行[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从文件中读取行而不在末尾附加“\ n”[重复]

用 .encode 和 utf8 读取行 [重复]

如果未在Chrome中定义变量,脚本会立即停止[重复]

如何找到由拆分函数创建的数组的长度[重复]

从线程中删除重复的行

Shell脚本的循环:重复执行命令