0

第一次在这里发帖!我真的需要帮助,我在谷歌上查看了这个问题,但无法找到对我有用的答案。所以这就是问题所在。我在 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 行,但循环似乎只执行了一次。我在屏幕上看到的只是一次“[此参数是必需的]”,并且模块刚刚启动(并且由于它没有所需的参数而崩溃......)。

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

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

Α。

4

1 回答 1

2

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

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

    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将它们拆分为字段,您可以简单echocut

    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
    

    ...但这会冒着参数被分词和/或扩展到文件列表的风险。

于 2017-05-13T01:24:27.130 回答