Команда "read" не выполняется в цикле "while read line"

Первый пост здесь! Мне действительно нужна помощь в этом, я посмотрел проблему в 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 строки, циклы, кажется, выполняются только один раз. Все, что я вижу на экране, это "[Этот аргумент обязателен]" только один раз, и модуль оправдывает запуск (и вылетает, потому что у него нет обязательных аргументов...).

На самом деле не знаю, что делать, здесь... Я надеюсь, что у кого-то здесь есть ответ ^^'. Заранее спасибо!

(и извините за возможные ошибки, я француз)

Альфа.

1 ответ

Решение

Как заметил @that другой парень в комментарии, проблема в том, что все read Команды в цикле читают из файла args.conf, а не пользователя. Я бы справился с этим, перенаправив файл conf через дескриптор файла, отличный от stdin (fd #0); Мне нравится использовать FD #3 для этого:

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

(Примечание: если ваша оболочка read команда не понимает -u вариант, использовать read line <&3 вместо.)

В этом сценарии есть ряд других вещей, против которых я бы порекомендовал:

  • Переменные ссылки без двойных кавычек, например echo $line вместо echo "$line", а также < modules/$name/args.conf вместо < "modules/$name/args.conf", Ссылки на переменные без кавычек разделяются на слова (если они содержат пробелы), а любые символы подстановки, которые совпадают с именами файлов, заменяются списком подходящих файлов. Это может вызвать действительно странные и периодические ошибки. К сожалению, ваше использование $argline зависит от разделения слов на несколько аргументов; если вы используете bash (не универсальная оболочка POSIX), вместо этого вы можете использовать массивы; Я доберусь до этого.

  • Вы используете относительные пути к файлам везде, и 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"
    

    (Кстати, обратите внимание, что я использую $() вместо галочек. Их легче читать, и у них нет каких-то тонких синтаксических странностей, которые есть у обратных галочек. Я рекомендую переключение.)

  • Кстати говоря, вы злоупотребляете временными файлами; многое из того, с чем вы работаете, может быть отлично сделано с помощью переменных оболочки и встроенных функций оболочки. Например, вместо того, чтобы читать строки из файла конфигурации, затем сохранять их во временном файле и использовать cut разделить их на поля можно просто echo в cut:

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

    ... или еще лучше, используйте readвстроенная возможность разбивать поля на основе чего угодно IFS установлен в:

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

    (Обратите внимание, что с момента назначения IFS это префикс к read команда, это влияет только на эту одну команду; изменения IFS во всем мире может иметь странные последствия, и его следует избегать, когда это возможно.)

    Точно так же, сохраняя список аргументов в файле, конвертируя строки в пробелы в другой файл, затем читая этот файл... вы можете пропустить любой или все эти шаги. Если вы используете bash, сохраните список аргументов в массиве:

    arglist=()
    while ...
        arglist+=("$answer") # or ("#arg=$answer")? Not sure of your syntax.
    done ...
    
    "$module_root/modules/$name/$interpreter" "$file" "${arglist[@]}"
    

    (Этот беспорядочный синтаксис с двойными кавычками, фигурными скобками, квадратными скобками и знаком-знаком является в целом правильным способом расширения массива в bash).

    Если вы не можете рассчитывать на bash расширения, такие как массивы, вы можете, по крайней мере, сделать это по-старому, используя простую переменную:

    arglist=""
    while ...
        arglist="$arglist $answer" # or "$arglist $arg=$answer"? Not sure of your syntax.
    done ...
    
    "$module_root/modules/$name/$interpreter" "$file" $arglist
    

    ... но это может привести к тому, что аргументы будут разбиты на слова и / или расширены до списков файлов.

Другие вопросы по тегам