Захват нескольких выходных строк в переменную Bash

у меня есть скрипт "myscript", который выводит следующее:

abc
def
ghi

в другом сценарии я вызываю:

declare RESULT=$(./myscript)

и $RESULT получает значение

abc def ghi

есть ли способ сохранить результат либо с помощью новых строк, либо с символом "n", чтобы я мог вывести его с помощью"echo -e'?

6 ответов


на самом деле, результат содержит то, что вы хотите-продемонстрировать:

echo "$RESULT"

то, что вы показываете, это то, что вы получаете от:

echo $RESULT

как отмечено в комментариях, разница заключается в том, что (1) двукратная версия переменной (echo "$RESULT") сохраняет внутренний интервал значения точно так, как он представлен в переменной - newlines, tabs, multiple blanks и all - while (2) unquoted version (echo $RESULT) заменяет каждую последовательность из одного или более пробелов, табуляций и строки с пробелом. Таким образом, (1) сохраняет форму входной переменной, тогда как (2) создает потенциально очень длинную одну строку вывода с "словами", разделенными одиночными пробелами (где "слово" -это последовательность символов без пробелов; в любом из слов не должно быть буквенно-цифровых символов).


еще одна ловушка с этим является то, что команду - $() - полосы, заканчивающиеся новыми линиями. Наверное, не всегда важно, но если вы действительно хотите сохранить ровно что было выведено, вам придется использовать другую строку и некоторые цитаты:

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

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


в дополнение к ответу, данному @l0b0, у меня просто была ситуация, когда мне нужно было сохранить любые конечные новые строки, выводимые скриптом и проверьте код возврата скрипта. И проблема с ответом l0b0 заключается в том, что "echo x" сбрасывал $? назад к нулю... так что мне удалось придумать это очень хитрое решение:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"

если вас интересуют определенные строки, используйте result-array:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"

Как насчет этого, он будет читать каждую строку в переменную, и это может быть использовано впоследствии ! скажем, вывод myscript перенаправляется в файл с именем myscript_output

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'

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

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

быстрый взлом, чтобы сделать это сделать запрошенное действие:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

Примечание это добавляет дополнительную строку. Если вы работаете над этим, вы можете кодировать вокруг него, я просто слишком ленив.


EDIT: хотя этот случай работает отлично, люди, читающие это, должны знать, что вы можете легко раздавить свой stdin внутри цикла while, тем самым давая вам скрипт, который будет запускать одну строку, очистить stdin и выйти. Как ssh сделает это, я думаю? Я только что видел это недавно, другие примеры кода здесь: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

еще раз! На этот раз с другой файловой ручкой (stdin, stdout, stderr равны 0-2, поэтому мы можем использовать &3 или выше в bash).

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

вы также можете использовать mktemp, но это лишь небольшой пример кода. Использование для mktemp выглядит так:

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

затем используйте $filenamevar, как и фактическое имя файла. Наверное, не нужно объяснил здесь, но кто-то пожаловался в комментариях.