7

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

5

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

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 нет пробелов, но откуда берется лишний пробел?

5 ответ(ов)

10

Heredoc действительно звучит более удобно для этой цели. Он используется для отправки нескольких команд в интерпретатор команд, такой как ex или cat.

Пример использования heredoc с командой cat:

cat << EndOfMessage
Это строка 1.
Это строка 2.
Строка 3.
EndOfMessage

Строка после << указывает, где нужно остановиться.

Чтобы отправить эти строки в файл, вы можете использовать:

cat > $FILE <<- EOM
Строка 1.
Строка 2.
EOM

Также вы можете сохранить эти строки в переменной:

read -r -d '' VAR << EOM
Это строка 1.
Это строка 2.
Строка 3.
EOM

Это сохраняет строки в переменной с именем VAR.

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

echo "$VAR"

Еще лучше, вы можете использовать отступы, чтобы сделать код более читабельным. В этом случае просто добавьте - после <<, чтобы табуляция не отображалась.

read -r -d '' VAR <<- EOM
    Это строка 1.
    Это строка 2.
    Строка 3.
EOM

Однако в этом случае вы должны использовать табуляции, а не пробелы, для отступов в вашем коде.

3

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

USAGE=$(cat <<-END
    Это первая строка.
    Это вторая строка.
    Это третья строка.
END
)

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

ПРИМЕЧАНИЯ:

  • Важно, чтобы закрывающая скобка находилась на другой строке. Текст END должен быть размещен на отдельной строке.
  • При использовании этой переменной, чтобы сохранить нечитаемые символы (\t, \n), необходимо заключить её в кавычки. То есть, используйте echo "$USAGE" (не: echo $USAGE).
1

Команда echo добавляет пробелы между переданными ей аргументами. Переменная $text подвергается расширению и разделению по словам, поэтому ваша команда echo эквивалентна следующей:

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

Как видно, пробел будет добавлен перед "this". Вы можете либо удалить символы новой строки и заключить $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
0

В вашем Bash-скрипте вы можете использовать оба предложенных способа для записи текста с переводами строк в файл.

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

#!/bin/sh

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

Этот код создаст файл filename, в котором будет текст с тремя строками, разделенными переводами строк.

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

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

Этот способ также даст тот же результат – файл filename будет содержать текст с правильными переводами строк.

В результате, если вы выполните команду cat filename, вы получите:

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

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

0

Вот несколько решений, которые я нашел, если вы хотите, чтобы каждая строка была правильно отступлена:

  1. Вы можете использовать echo:

    echo    "это первая строка"   \
        "\n""это вторая строка"   \
        "\n""это третья строка" \
        > имя_файла
    

    Это не сработает, если вы поставите "\n" прямо перед \ в конце строки.

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

    printf '%s\n' \
        "это первая строка"   \
        "это вторая строка"   \
        "это третья строка" \
        > имя_файла
    
  3. Еще одно решение может выглядеть так:

    text=''
    text="${text}это первая строка\n"
    text="${text}это вторая строка\n"
    text="${text}это третья строка\n"
    printf "%b" "$text" > имя_файла
    

    или

    text=''
    text+="это первая строка\n"
    text+="это вторая строка\n"
    text+="это третья строка\n"
    printf "%b" "$text" > имя_файла
    
  4. Еще одно решение можно получить, комбинируя printf и sed:

    if что-то
    then
        printf '%s' '
        это первая строка
        это вторая строка
        это третья строка
        ' | sed '1d;$d;s/^    //g'
    fi
    

    Сложно рефакторить код, отформатированный таким образом, так как вы жестко зашиваете уровень отступа в код.

  5. Также возможно использовать вспомогательную функцию и некоторые приемы подстановки переменных:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # Это пустая строка, которая демонстрирует логику использования 
    # "+" вместо ":+" в подстановке переменных выше.
    _ ""
    _ "это первая строка"
    _ "это вторая строка"
    _ "это третья строка"
    unset -f _
    printf '%s' "$text"
    

Выберите один из этих способов в зависимости от ваших потребностей и предпочтений.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь