Команда "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
... но это может привести к тому, что аргументы будут разбиты на слова и / или расширены до списков файлов.