“读取”命令不在“读取行”循环中执行

时间:2017-05-12 22:49:18

标签: linux bash while-loop line-by-line

首先发布在这里!我真的需要这方面的帮助,我在谷歌看了问题,但无法找到一个有用的答案给我。所以这就是问题所在。 我很乐意用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行,但循环似乎只执行一次。我在屏幕上看到的只是“[这个参数是必需的]”只是一次,并且模块刚刚启动(并且崩溃,因为它没有所需的参数......)。

真的不知道该怎么办,这里......我希望有人在这里有一个答案^^'。 提前谢谢!

(抱歉最终错误,我是法国人)

α

1 个答案:

答案 0 :(得分:2)

正如其他人在评论中指出的那样,问题是循环中的所有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在任何简单命令失败时退出脚本,但是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将它们拆分为字段,您只需echocut

    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
    

    ...但是这会冒成分词和/或扩展到文件列表的风险。