Як я можу мати новий рядок у рядку в ш?


339

Це

STR="Hello\nWorld"
echo $STR

виробляє як вихід

Hello\nWorld

замість

Hello
World

Що мені робити, щоб мати рядок у рядку?

Примітка. Це питання не стосується відлуння . Мені це відомо echo -e, але я шукаю рішення, яке дозволяє передавати рядок (що включає в себе новий рядок) як аргумент для інших команд, які не мають аналогічного варіанта інтерпретувати \n's як нові рядки.

Відповіді:


353

Рішення полягає у використанні $'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.

74
Це дійсно bash, але не POSIX sh.
jmanning2k

7
+ 1-лише примітка: export varDynamic = "$ varAbody1 $ varAbody2" $ '\ n'
Йордан Георгієв,

3
Це дійсно працює з буквальними рядками, як запитував ОП, але не вдається, як тільки підміняються змінні: STR=$"Hello\nWorld"просто друкується Hello\nWorld.
ssc

3
Одиночні цитати @ssc, а не подвійні
miken32

7
@ssc у вашому прикладі немає змінних. Також цей метод вимагає одинарних лапок. Нарешті, щоб включити інтерпольовані змінні, вам доведеться об'єднати рядки з подвійним котируванням та спеціальними одноцитованими разом. Наприклад H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}", лунатимуть "Привіт" та "Світ" на окремих рядках
JDS

166

Ехо настільки дев'яностих років і настільки загрожує небезпеками, що його використання повинно призвести до основних скидів не менше 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. Усі будь ласка, використовуйте printfзараз і ніколи не озирайтеся.


3
Дякую за цю відповідь, я думаю, що вона дає дійсно гарну пораду. Однак зауважте, що моє первісне запитання було насправді не про те, як отримати відлуння для друку нових рядків, а про те, як отримати новий рядок у змінну оболонки. Ви показуєте, як це зробити, використовуючи також printf, але я думаю, що рішення з використанням амфетамахіну$'string' ще чистіше.
Хуан А. Наварро

11
Лише для дефіцитного визначення поняття "чистий". $'foo'Синтаксис не діє синтаксис POSIX , як в 2013 р Багатьох снарядів будуть скаржитися.
Єнс

5
BAR=$(printf "hello\nworld\n")не надрукує для мене останнє \ n
Jonny,

3
@Jonny Це не повинно; специфікація оболонки для підстановки команд говорить, що один або більше нових рядків в кінці виводу видаляються. Я усвідомлюю, що це несподівано для мого прикладу, і відредагував його, щоб включити новий рядок у printf.
Єнс

5
@ismael Ні, $()задається 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

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.
Хуан А. Наварро

Це не відповідь на питання
Рохіт Гупта

Це повністю відповідає на питання, яке я шукав! Дякую!
Kieveli

1
Тут добре працювали для мене. Я використовував для створення файлу на основі цього відлуння, і він генерував нові рядки на виході правильно.
Матеус Леон

echo -eсумно відомий своїми проблемами з портативністю. Зараз це ситуація на Дикому Заході менше, ніж перед 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

4

Я не фахівець з басу, але цей працював на мене:

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

$STR2
EOF
)
echo "$NEWSTR"

Це мені було легше форматувати тексти.


2
Старий добрий гередок . І це, мабуть, також сумісний з POSIX!
піб

Лапки навколо $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як символ завершення. Це змушує роботу розбивання нового рядка навіть для закінчення виводу дос \r\n.

IFS="$(printf '\n\r')"

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у різних формах, тому я не впевнений, що це додає значення як окремої відповіді.
трійчатка

Це для коротких рядків, але якщо ви хочете, щоб речі були впорядковані, і кожен рядок насправді завдовжки 60 символів, це зробити акуратним способом. Зважаючи на те, що це рішення, з яким я врешті-решт пішов, воно щось додає, якщо не для вас, то для себе в майбутньому, коли я безперечно повернусь.
Адам К Дін

1
Навіть для довгих рядків я віддаю перевагу printf '%s\n' "first line" \ (новий рядок, відступ) 'second line'і т. Д. Дивіться також printf -v strпро присвоєння змінної без нерестування підпакеті.
трійка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.