Многострочная строка с дополнительным пробелом (сохраненный отступ)


410

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

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"
Джаеш Бхой

1
Удаление \nв конце каждой строки приводит к тому, что все выходные данные выводятся в одну строку.
Джонатан Хартли

18
Ага: Поместить двойные кавычки вокруг "$text"линии эха очень важно. Без них ни один из символов новой строки (как буквальный, так и '\ n') не работает. С ними они все делают.
Джонатан Хартли,

Ответы:


663

Эредок звучит для этого удобнее. Он используется для отправки нескольких команд в программу интерпретатора команд, например 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

8
Как я могу сделать чтение выхода со статусом 0? Кажется, что он не может найти конец строки (так как это -d '') и завершается с 1, что не помогает, когда вы находитесь в скрипте.
Миша Слюсарев

7
@MishaSlyusarev используйте -d '\ 0' и добавьте \ 0 в конце вашей последней строки
Ларс Шнайдер

11
Использование -dопции в read -r -d '' VARIABLE <<- EOMне работало для меня.
Дрон22

186

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

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

Если вы сделаете отступ для строки с помощью табуляции (то есть '\ t'), отступ будет удален. Если вы сделаете отступ с пробелами, отступ останется в.

ПРИМЕЧАНИЕ: Это является существенным , что последняя закрывающая скобка находится на другой линии. ENDТекст должен появиться на отдельной строке.


1
Это важно? Работает на моем Mac с )той же линии. Я думаю, это потому, что то, что происходит между $(и )будет выполняться в своей собственной вселенной, и настоящая команда не увидит ')' в любом случае. Интересно, работает ли это и на других?
Диджей

Возможно, что разные оболочки будут интерпретировать вещи по-разному. В документации bash , похоже, ничего не сказано так или иначе, но во всех примерах это написано на отдельной строке.
Эндрю Майнер

Забавно, я нашел ответ, когда пытался установить значение для переменной USAGE. Так что ваш ответ точно соответствует. :)
Буник

1
@deej Из спецификации POSIX : здесь-документ ... продолжается до тех пор, пока не появится строка, содержащая только разделитель и <newline>
Fornost

1
@Fornost Это не противоречит тому, что я сказал
Диджей

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

имя файла кошки дает:

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

Кажется, что альтернативный трюк работает в то время как Shell Scripts, но он не работает для bash.
Macindows

3
@ Windows, кажется, я забыл -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. Когда мне все равно, я просто держу строку в кавычках открытой над несколькими строками.
Пизис,

Очень хороший ответ, особенно 3b
Сумит Трехан

Msgstr "Это не работает, если вы ставите" \ 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 
 Guga 
   Пугачев 

Все начальные и конечные пробелы будут сохранены

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.