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

419

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

abc
def
ghi

в другом script, я вызываю:

declare RESULT=$(./myscript)

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

abc def ghi

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

  • 1
    это удивляет меня у вас нет $ (cat ./myscipt)? иначе я бы ожидал, что он попытается выполнить команды abc, def и ghi
  • 0
    @litb: да, я так думаю; Вы также можете использовать $ (<./ myscript), который избегает выполнения команды.
Показать ещё 1 комментарий
Теги:
variables

6 ответов

746
Лучший ответ

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

echo "$RESULT"

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

echo $RESULT

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

  • 62
    @troelskn: разница в том, что (1) версия переменной в двойных кавычках сохраняет внутренний интервал значения точно так, как он представлен в переменной, символах новой строки, табуляции, нескольких пробелах и т. д., тогда как (2) версия без кавычек заменяет каждая последовательность из одного или нескольких пробелов, табуляции и новых строк с одним пробелом. Таким образом, (1) сохраняет форму входной переменной, тогда как (2) создает потенциально очень длинную единственную строку вывода с «словами», разделенными пробелами (где «слово» - это последовательность непробельных символов; Никаких буквенно-цифровых символов в любом из слов).
  • 20
    Чтобы облегчить понимание ответа: ответ говорит, что echo "$ RESULT" сохраняет символ новой строки, а echo $ RESULT - нет.
Показать ещё 3 комментария
72

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

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

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

  • 3
    +1 Это правда и то, чего не хватало в ответе Джонатана Леффлера!
  • 4
    Мне пришлось некоторое время работать со сломанной оболочкой, которая не удаляла последний символ новой строки из подстановки команд (это не подстановка процессов ), и это сломало почти все. Например, если вы сделали pwd=`pwd`; ls $pwd/$file , вы получили новую pwd=`pwd`; ls $pwd/$file перед / и заключение в двойные кавычки не помогло. Это было исправлено быстро. Это было в 1983-5 годах на ICL Perq PNX; в оболочке не было $PWD как встроенной переменной.
12

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

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"
  • 0
    Это прекрасно, у меня действительно был случай использования, где я хочу иметь функцию die () которая принимает произвольный код завершения и, необязательно, сообщение, и я хотел использовать другую функцию use usage () для предоставления сообщения, но переводы строки продолжали получать раздавленный, надеюсь, это позволит мне обойти это.
11

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

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"
  • 3
    Если в строках есть пробелы, это будет считать поля (содержимое между пробелами), а не строки.
  • 3
    это не работает на Dash или POSIX Shell
Показать ещё 1 комментарий
0

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

./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, тем самым предоставляя вам script, который будет запускать одну строку, очистить 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
    Я пробовал и другие решения, с вашим первым предложением я наконец-то получил свой скрипт
  • 1
    Downvote: это слишком сложно и не позволяет избежать множества распространенных ошибок в bash .
Показать ещё 2 комментария
0

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

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'
  • 4
    Ну, это не Баш, это ужасно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню