Захват многострочного вывода в переменную Bash

StackOverflow https://stackoverflow.com/questions/613572

  •  03-07-2019
  •  | 
  •  

Вопрос

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

abc
def
ghi

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

declare RESULT=$(./myscript)

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

abc def ghi

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

Это было полезно?

Решение

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

echo "$RESULT"

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

echo $RESULT
<Ч>

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

Другие советы

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

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

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

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

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

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

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

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

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

<Ч>

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top