Как получить перевод строки в sh?

340

это

STR="Hello\nWorld"
echo $STR

производит как выход

Hello\nWorld

вместо того

Hello
World

Что я должен сделать, чтобы иметь новую строку в строке?

Примечание: этот вопрос не об эхо . Я знаю echo -e, но я ищу решение, которое позволяет передавать строку (которая включает символ новой строки) в качестве аргумента другим командам, у которых нет аналогичной опции для интерпретации \nкак строк новой строки.

Хуан А. Наварро
источник

Ответы:

354

Решение заключается в использовании $'string', например:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

Вот выдержка из страницы руководства Bash:

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.
amphetamachine
источник
74
Это действительно Bash, но не POSIX sh.
jmanning2k
7
+ 1-только примечание: export varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Йордан Георгиев
3
Это действительно работает с литеральными строками, как запросил OP, но терпит неудачу, как только переменные должны быть заменены: STR=$"Hello\nWorld"просто печатает Hello\nWorld.
2016 г.
3
@ssc одинарные кавычки, а не двойные
miken32
7
@ssc в вашем примере нет переменных. Также этот метод требует одинарных кавычек. Наконец, чтобы включить интерполированные переменные, вы должны объединить строки в двойных и специальных одинарных кавычках. например, H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"будет отображать «Hello» и «World» в отдельных строках
JDS
166

Echo настолько девяностых и настолько чреват опасностями, что его использование должно привести к дампам ядра не менее 4 ГБ. Серьезно, проблемы с эхо были причиной того, что процесс стандартизации Unix, наконец, изобрел printfутилиту, покончив со всеми проблемами.

Итак, чтобы получить новую строку в строке:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

Там! Нет SYSV против BSD эха безумия, все это аккуратно напечатано и полностью переносимая поддержка C escape-последовательностей. Все, пожалуйста, используйте printfсейчас и никогда не оглядывайся назад.

Jens
источник
3
Спасибо за этот ответ, я думаю, что он дает действительно хороший совет. Однако обратите внимание, что мой первоначальный вопрос был не о том, как получить эхо для вывода новых строк, а о том, как получить новую строку в переменной оболочки. Вы показываете, как это сделать, используя printf, но я думаю, что решение от использования амфетамина$'string' еще чище.
Хуан А. Наварро
11
Только для неполного определения «чистый». $'foo'Синтаксис не действует синтаксис POSIX , как в 2013 г. Многих снарядов будут жаловаться.
Jens
5
BAR=$(printf "hello\nworld\n")не печатает завершающий \ n для меня
Джонни
3
@ Джонни Это не должно; спецификация оболочки для подстановки команд гласит, что одна или несколько строк новой строки в конце вывода удаляются. Я понимаю, что это неожиданно для моего примера, и отредактировал его, добавив новую строку в printf.
Дженс
5
@ismael No, $()указывается POSIX как удаление последовательностей из одного или нескольких символов <newline> в конце подстановки .
Дженс
61

На основании других ответов я сделал

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight
Zvezda
источник
30

Проблема не с оболочкой. Проблема на самом деле с самой echoкомандой и отсутствием двойных кавычек вокруг переменной интерполяции. Вы можете попробовать использовать, echo -eно это не поддерживается на всех платформах, и одна из причин printfтеперь рекомендуется для переносимости.

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

#!/bin/sh
echo "Hello
World"
#EOF

или эквивалентно

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
аллюр
источник
24

Я нахожу -eфлаг элегантным и прямым

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

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

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
Саймон Ндунда
источник
2
Это уже упоминалось в других ответах. Кроме того, хотя это уже было, я только что отредактировал вопрос, чтобы подчеркнуть тот факт, что этот вопрос не о echo.
Хуан А. Наварро
Это не ответ на вопрос
Рохит Гупта
Это полностью отвечает на вопрос, который я искал! Спасибо!
Кьевели
1
Хорошо сработало для меня здесь. Я использовал для создания файла на основе этого эха, и он генерировал новые строки на выходе правильно.
Матеус Леон
echo -eизвестен своими проблемами переносимости. Сейчас ситуация на Диком Западе меньше, чем в pre-POSIX, но нет никаких оснований для этого echo -e. См. Также stackoverflow.com/questions/11530203/…
tripleee
21
  1. Единственная простая альтернатива - ввести новую строку в переменной:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line

    Да, это означает, что Enterнужно писать в коде, где это необходимо.

  2. Есть несколько эквивалентов new lineперсонажа.

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.

    Но все они требуют «интерпретации» каким-либо инструментом ( POSIX printf ):

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    И поэтому инструмент необходим для построения строки с новой строкой:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. В некоторых оболочках последовательность $'представляет собой специальное расширение оболочки. Известно, что работает в ksh93, bash и zsh:

    $ STR=$'new\nline'
  4. Конечно, возможны и более сложные решения:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    Или

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

источник
6

Знак $ перед одинарными кавычками '... \ n ...' выглядит следующим образом, однако двойные кавычки не работают.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
caot
источник
4

Я не эксперт по Bash, но этот работал для меня:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

Я нашел это проще для форматирования текстов.

Джейсон
источник
2
Старый добрый наследник . И это, вероятно, POSIX-совместимый тоже!
pyb
Кавычки вокруг $NEWSTRв echo "$NEWSTR"здесь важны
Шади
0

В моей системе (Ubuntu 17.10) ваш пример работает так, как нужно, как при вводе из командной строки (в sh), так и при выполнении в виде shскрипта:

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

Я думаю, что это отвечает на ваш вопрос: это просто работает. (Я не пытался выяснить детали , такие , как , в какой момент именно замены символа новой строки для \nпроисходит в sh).

Тем не менее, я заметил, что этот же скрипт будет работать по-разному при выполнении сbash и Hello\nWorldвместо этого будет распечатывать :

[bash bash test-str.sh
Hello\nWorld

Мне удалось получить желаемый результат bashследующим образом:

[bash STR="Hello
> World"
[bash echo "$STR"

Обратите внимание на двойные кавычки $STR. Это ведет себя одинаково, если сохранить и запустить как bashскрипт.

Следующее также дает желаемый результат:

[bash echo "Hello
> World"
Алексей
источник
0

Те требовательные, которые нуждаются только в новой строке и презирают многострочный код, который ломает отступы, могут сделать:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

Bash (и, вероятно, другие оболочки) сожирает все завершающие символы новой строки после подстановки команды, поэтому вам нужно завершить printfстроку не символом новой строки и затем удалить ее. Это также может легко стать вкладчиком.

IFS="$(printf '\nx')" IFS="${IFS%x}"

Я знаю, что это два действия вместо одного, но мой OCD с отступами и переносимостью сейчас в порядке :) Я изначально разработал это, чтобы иметь возможность разделять вывод, разделенный только на новую строку, и в итоге я использовал модификацию, которая использует \rв качестве завершающего символа. Это заставляет разделение новой строки работать даже для вывода DOS, заканчивающегося на \r\n.

IFS="$(printf '\n\r')"
tlwhitec
источник
0

Я не был действительно счастлив ни с одним из вариантов здесь. Это то, что сработало для меня.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
Адам К Дин
источник
Это очень утомительный способ написания str=$(printf '%s\n' "first line" "another line" "and another line")(оболочка будет удобно (или нет) обрезать любые последние символы новой строки из подстановки команд). Несколько ответов здесь уже продвигаются printfв различных формах, поэтому я не уверен, что это добавляет какую-то ценность как отдельный ответ.
tripleee
Это для коротких строк, но если вы хотите, чтобы все было аккуратно и каждая строка на самом деле была длиной 60 символов, то это аккуратный способ сделать это. Учитывая, что это решение, с которым я в конце концов пошел, оно добавляет что-то, если не для вас, то для меня в будущем, когда я, несомненно, вернусь.
Адам К Дин
1
Даже для длинных строк я предпочитаю printf '%s\n' "first line" \ (перевод строки, отступ) 'second line'и т. Д. См. Также printf -v strдля присвоения переменной без порождения подоболочки.
tripleee