"read" 命令未在 "while read line" 循环中执行
"read" command not executing in "while read line" loop
首先post这里!我真的需要这方面的帮助,我在 google 上查看了这个问题,但找不到对我有用的答案。所以这就是问题所在。
我在 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 行,但循环似乎只执行了一次。我在屏幕上看到的只是一次“[此参数是必需的]”,模块刚刚启动(并且崩溃,因为它没有所需的参数......)。
真的不知道怎么办,在这里...希望这里有人能回答^^'。
提前致谢!
(对于最终的错误,我很抱歉,我是法国人)
阿尔法。
正如@that other guy 在评论中指出的那样,问题是循环中的所有 read
命令都是从 args.conf 文件而不是用户读取的。我处理这个问题的方法是通过与 stdin (fd #0) 不同的文件描述符重定向 conf 文件;我喜欢为此使用 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"
。未加引号的变量引用被拆分成单词(如果它们包含空格),任何恰好与文件名匹配的通配符将被匹配文件列表替换。这可能会导致 really 怪异和间歇性错误。不幸的是,您对 $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
您正在当前目录中创建临时文件,这有随机冲突的风险(同时与脚本的其他 运行s,任何文件恰好有您的名字'正在使用等)。最好在开始时创建一个临时目录,然后将所有内容存储在其中(同样,通过绝对路径):
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
...但这 运行 参数被分词 and/or 扩展到文件列表的风险。
首先post这里!我真的需要这方面的帮助,我在 google 上查看了这个问题,但找不到对我有用的答案。所以这就是问题所在。 我在 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 行,但循环似乎只执行了一次。我在屏幕上看到的只是一次“[此参数是必需的]”,模块刚刚启动(并且崩溃,因为它没有所需的参数......)。
真的不知道怎么办,在这里...希望这里有人能回答^^'。 提前致谢!
(对于最终的错误,我很抱歉,我是法国人)
阿尔法。
正如@that other guy 在评论中指出的那样,问题是循环中的所有 read
命令都是从 args.conf 文件而不是用户读取的。我处理这个问题的方法是通过与 stdin (fd #0) 不同的文件描述符重定向 conf 文件;我喜欢为此使用 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"
。未加引号的变量引用被拆分成单词(如果它们包含空格),任何恰好与文件名匹配的通配符将被匹配文件列表替换。这可能会导致 really 怪异和间歇性错误。不幸的是,您对$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
您正在当前目录中创建临时文件,这有随机冲突的风险(同时与脚本的其他 运行s,任何文件恰好有您的名字'正在使用等)。最好在开始时创建一个临时目录,然后将所有内容存储在其中(同样,通过绝对路径):
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
...但这 运行 参数被分词 and/or 扩展到文件列表的风险。