Багаторядковий рядок з додатковим простором (збережений відступ)


409

Я хочу написати кілька попередньо визначених текстів у файл із наступним:

text="this is line one\n
this is line two\n
this is line three"

echo -e $text > filename

Я очікую щось подібне:

this is line one
this is line two
this is line three

Але отримав це:

this is line one
 this is line two
 this is line three

Я впевнений, що немає місця після кожного \n, але як виходить зайвий простір?


2
Я не впевнений, але .. як якщо ви просто ввели text="this is line one\nthis is line two\nthis is line three"один і той же рядок ..? (без жодного входу)
Йоханес Хосіяван

4
Видаліть \nна кожному рядку, ви вже натиснули новий рядок, щоб перейти до нового рядка
Марк Сетчелл

Ви вже дали \n. Отже, чому ви ставите наступний рядок у новому рядку? Простоtext="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi

1
Видалення \nв кінці кожного рядка призводить до того, що всі результати працюють разом в одному рядку.
Джонатан Хартлі

18
Ага: Розміщення подвійних лапок навколо "$text"рядка відлуння є вирішальним. Без них жоден з нових рядків (як буквальний, так і \ \ n ') не працює. З ними все роблять.
Джонатан Хартлі

Відповіді:


662

З цією метою Гередок звучить зручніше. Він використовується для надсилання декількох команд програмі інтерпретатора команд, наприклад, ex або cat

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

Рядок після <<вказує, де зупинитися.

Щоб надіслати ці рядки до файлу, використовуйте:

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

Ви також можете зберегти ці рядки до змінної:

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

Це зберігає рядки до змінної з назвою VAR.

Під час друку пам’ятайте лапки навколо змінної, інакше символів нового рядка ви не побачите.

echo "$VAR"

Ще краще, ви можете використовувати відступи, щоб вони виділялися більше у вашому коді. Цього разу просто додайте позначку " -Після", <<щоб запобігти появі вкладок.

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

Але тоді для відступу у вашому коді потрібно використовувати вкладки, а не пробіли.


38
чому у вас << - замість << у першому рядку 2-го та 5-го кодового блоку? чи робить щось особливе?
lohfu

35
@ sup3rman '-' Ігнорує провідні вкладки. Див stackoverflow.com/questions/2500436 / ...
Kervin

7
як я можу зробити вихід з читання зі статусом 0? Здається, він не може знайти кінець рядка (оскільки це -d '') і виходить з 1, що не допомагає, коли ви знаходитесь в сценарії.
Міша Слюсарев

6
@MishaSlyusarev використовуйте -d '\ 0' і додайте \ 0 в кінці останнього рядка
Ларс Шнайдер

11
Використання -dпараметра в read -r -d '' VARIABLE <<- EOMмені не працювало.
dron22

186

Якщо ви намагаєтеся ввести рядок у змінну, іншим простим способом є щось подібне:

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

Якщо відступити рядок за допомогою вкладок (тобто '\ t'), відступ буде викреслено. Якщо ви маєте відступ із пробілами, відступ залишиться в.

ПРИМІТКА. Важливо , що остання дужка, що закривається, знаходиться в іншому рядку. ENDТекст повинен з'явитися на окремому рядку.


1
Це важливо? Працює над моїм Mac )на тій же лінії. Я думаю, це тому, що те, що відбувається між $(і )збирається виконати у власному Всесвіті, а фактична команда все одно не побачить ")". Цікаво, чи працює він і для інших.
deej

Цілком можливо, що різні оболонки будуть тлумачити речі по-різному. У документації на bash, схоже, нічого не сказано так чи інакше, але всі приклади мають це в своєму рядку.
Андрій Шахтар

Цікаво, що я знайшов відповідь, коли намагався встановити значення для змінної USAGE. Отже, ваша відповідь точно відповідає. :)
Bunyk

1
@deej Із специфікації POSIX : Цей документ… продовжується, поки не з’явиться рядок, що містить лише роздільник та <newline>
Fornost

1
@Fornost Це не суперечить тому, що я сказав
deej

84

echoдодає пробіли між переданими до нього аргументами. $textпідлягає змінному розширенню та розбиттю слів, тому ваша echoкоманда еквівалентна:

echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n"  ...

Ви можете бачити, що перед "цим" буде додано пробіл. Ви можете видалити символи нового рядка та процитувати, $textщоб зберегти нові рядки:

text="this is line one
this is line two
this is line three"

echo "$text" > filename

Або ви можете використовувати printf, який є більш надійним і портативним, ніж echo:

printf "%s\n" "this is line one" "this is line two" "this is line three" > filename

У системі bash, яка підтримує розширення дужок, ви навіть можете:

printf "%s\n" "this is line "{one,two,three} > filename

1
Дякуємо за пояснення, чому мод бачить додатковий простір при використанні команди "echo". Ця відповідь також добре працює при використанні в bash скрипті (на відміну від інтерактивного сеансу оболонки). Відчуйте, що це справжня відповідь і має бути прийнятою відповіддю.
onelaview

1
На це слід прийняти відповідь. Усі інші відповіді давали безліч цікавих інших способів вирішити біль від ОП, але технічно питання полягало у тому, як "з'являється зайвий простір", і ніхто не звертався до цього :-) !!! Дякую, Джош, що зробив -1 таємницю!
Дмитро Шевкопляс

45

в bash-скрипті такі роботи:

#!/bin/sh

text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename

альтернативно:

text="this is line one
this is line two
this is line three"
echo "$text" > filename

ім'я файлу cat дає:

this is line one
this is line two
this is line three

Здається, альтернативний трюк працює під час Shell Scripts, але, схоже, він не працює на баш.
Macindows

3
@Macindows Я, здається, забув -eваріант для echo. Будь ласка спробуйте ще раз.
Кріс

41

Я знайшов більше рішень, оскільки хотів, щоб кожен рядок був належним чином відведений:

  1. Ви можете використовувати echo:

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename

    Це не спрацює, якщо ви поставили "\n"трохи раніше \в кінці рядка.

  2. Крім того, ви можете використовувати printfдля кращої портативності (у мене виникло багато проблем echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. Ще одне рішення може бути:

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this is line three\n"
    printf "%b" "$text" > filename

    або

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
  4. Інше рішення досягається змішуванням printfі sed.

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi

    Непросто переформатувати код, що відформатовується так, як ви жорстко кодуєте рівень відступу до коду.

  5. Можна скористатися допоміжною функцією та декількома прийомами заміни:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"

3b є моїм кращим рішенням, коли хочеться зберегти відступ коду та відступ рядків незалежно, принаймні, коли я не можу використовувати 2 у присвоєнні змінної, і це читабельніше, ніж 3a. Коли мені байдуже, я просто тримаю цитований рядок відкритим через кілька рядків.
Pysis

Дуже приємна відповідь, особливо 3b
Суміт Трехан

"Не працює, якщо ви поставите" \ n "безпосередньо перед \ в кінці рядка." - це чудова порада при використанні ехо.
Ноам Манос

6

Далі - мій кращий спосіб присвоїти багаторядковій рядку змінній (я думаю, це виглядає добре).

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

Кількість підкреслень однакова (тут 80) в обох випадках.


0

Просто згадуємо просту однолінійну конкатенацію, яка може бути корисною іноді.

# for bash

v=" guga "$'\n'"   puga "

# Just for an example.
v2="bar "$'\n'"   foo "$'\n'"$v"

# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar ${n}   foo ${n}$v"

echo "$v3" 

Ви отримаєте щось подібне

бар 
   foo 
 гуга 
   пуга 

Усі провідні та кінцеві проміжки білого кольору будуть збережені правильно

echo "$v3" > filename

0

Існує багато способів зробити це. Для мене чудово працює трубопровід з відступом у sed .

printf_strip_indent() {
   printf "%s" "$1" | sed "s/^\s*//g" 
}

printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"

Ця відповідь ґрунтувалася на відповіді Матеуша Піотровського , але трохи уточнила.


-1

він буде працювати, якщо ви поставите його нижче:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.