Захват многострочного вывода в переменную Bash - PullRequest
509 голосов
/ 05 марта 2009

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

abc
def
ghi

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

declare RESULT=$(./myscript)

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

abc def ghi

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

Ответы [ 6 ]

955 голосов
/ 05 марта 2009

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

echo "$RESULT"

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

echo $RESULT

Как отмечается в комментариях, разница в том, что (1) версия переменной в двойных кавычках (echo "$RESULT") сохраняет внутренний интервал значения в точности так, как он представлен в переменной - новые строки, табуляции, множественные пробелы и все - тогда как (2) версия без кавычек (echo $RESULT) заменяет каждую последовательность из одного или нескольких пробелов, табуляций и строк новой строки одним пробелом. Таким образом, (1) сохраняет форму входной переменной, в то время как (2) создает потенциально очень длинную единственную строку вывода с «словами», разделенными пробелами (где «слово» - это последовательность непробельных символов; никаких буквенно-цифровых символов в любом из слов).

84 голосов
/ 16 марта 2011

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

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

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

17 голосов
/ 12 июля 2013

Если вас интересуют конкретные строки, используйте массив результатов:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"
15 голосов
/ 11 июля 2013

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

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"
0 голосов
/ 04 мая 2016

Попробовав большинство решений здесь, я обнаружил, что проще всего было использовать временный файл. Я не уверен, что вы хотите сделать с многострочным выводом, но вы можете справиться с ним построчно, используя 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

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


РЕДАКТИРОВАТЬ: Хотя этот случай работает очень хорошо, люди, читающие это, должны знать, что вы можете легко сжать свой 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 так же, как действительное имя файла. Возможно, здесь не нужно объяснять, но кто-то жаловался в комментариях.

0 голосов
/ 21 мая 2012

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

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...