“读取”命令未在“读取行”循环中执行[重复]
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 line
和read -p " $arg" answer
正在争夺相同的数据。您是否尝试从用户那里阅读?最简单的破解方法是read -p " $arg=" answer < /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 <&3
。)
我建议反对此脚本中的许多其他内容:
变量引用不带双引号,例如echo $line
代替 echo "$line"
,< modules/$name/args.conf
代替 < "modules/$name/args.conf"
。未加引号的变量引用被拆分为单词(如果它们包含空格),并且恰好匹配文件名的任何通配符将被匹配文件列表替换。这可能会导致真的奇怪和间歇性的错误。不幸的是,您对$argline
的使用依赖于分词来分隔多个参数;如果您使用的是bash
(不是通用的 POSIX shell),您可以使用数组来代替;我会解决的。
您在各处都使用相对文件路径,并且在脚本中使用cd
ing。这往往是脆弱和令人困惑的,因为脚本中不同位置的文件路径不同,并且用户传入的任何相对路径在脚本cd
s 在其他地方第一次时将变得无效。更糟糕的是,当您 cd
时,您并没有检查错误,因此如果任何 cd
由于任何原因失败,那么整个脚本的其余部分将在错误的位置运行并奇怪地失败。你最好弄清楚系统的根目录在哪里(作为绝对路径),然后引用其中的所有内容(例如< "$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
(请注意,由于分配给IFS
是read
命令的前缀,它只影响那个命令;全局更改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)" 东西,我通常总是使用它,但我不知道为什么,当我在这个一段代码,上面写着“找不到命令剪切”......我必须按照我的方式使用它,否则它不起作用......无论如何,谢谢你们的回答,现在就试试!以上是关于“读取”命令未在“读取行”循环中执行[重复]的主要内容,如果未能解决你的问题,请参考以下文章