Bash продолжения

Как вы используете строки продолжения bash?

Я понимаю, что вы можете это сделать:

echo "continuation \
lines"
>continuation lines

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

    echo "continuation \
    lines"
>continuation     lines

Ответ 1

Это то, что вы можете пожелать

$       echo "continuation"\
>       "lines"
continuation lines

Если это создает два аргумента для эха, и вы их только хотите, тогда давайте посмотрим на конкатенацию строк. В bash, помещая две строки рядом друг с другом, конкатенатете:

$ echo "continuation""lines"
continuationlines

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

$ echo "continuation"\
> "lines"
continuationlines

Но когда используется отступ:

$       echo "continuation"\
>       "lines"
continuation lines

Вы получаете два аргумента, потому что это уже не конкатенация.

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

$ a="continuation"
$ b="lines"
$ echo $a$b
continuationlines

Это позволит вам иметь чистый код с отступом за счет дополнительных переменных. Если вы сделаете переменные локальными, это не должно быть слишком плохо.

Ответ 2

Здесь документы с терминатором <<-HERE хорошо работают для многострочных текстовых строк с отступом. Это удалит все ведущие вкладки из документа здесь. (Ограничители строки все равно останутся.)

cat <<-____HERE
    continuation
    lines
____HERE

Смотри также http://ss64.com/bash/syntax-here.html

Если вам нужно сохранить некоторые, но не все ведущие пробелы, вы можете использовать что-то вроде

sed 's/^  //' <<____HERE
    This has four leading spaces.
    Two of them will be removed by sed.
____HERE

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

tr -d '\012' <<-____
    continuation
     lines
____

(Во второй строке есть вкладка и пробел впереди; вкладка будет удалена оператором тире перед терминатором heredoc, тогда как пробел будет сохранен.)

Для наложения длинных сложных строк на несколько строк мне нравится printf:

printf '%s' \
    "This will all be printed on a " \
    "single line (because the format string " \
    "does not specify any newline)"

Это также хорошо работает в тех случаях, когда вы хотите встроить нетривиальные фрагменты сценария оболочки в другой язык, где синтаксис основного языка не позволяет использовать документ здесь, например, в Makefile или Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh' \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript

Ответ 3

Вы можете использовать массивы bash

$ str_array=("continuation"
             "lines")

затем

$ echo "${str_array[*]}"
continuation lines

есть дополнительный пробел, потому что (после руководства bash):

Если слово в двойных кавычках, ${name[*]} расширяется до одного слова с значение каждого члена массива, разделенного первым символом Переменная IFS

Поэтому установите IFS='', чтобы избавиться от лишнего пространства

$ IFS=''
$ echo "${str_array[*]}"
continuationlines

Ответ 4

Я столкнулся с ситуацией, когда мне пришлось отправить длинное сообщение как часть аргумента команды и пришлось придерживаться ограничения длины строки. Команды выглядят примерно так:

somecommand --message="I am a long message" args

То, как я решил это, - это переместить сообщение как документ здесь (например, предложенный вариант @tripleee). Но здесь документ становится stdin, поэтому его нужно прочитать снова, я пошел с подходом ниже:

message=$(
    tr "\n" " " <<- END
        This is a
        long message
END
)
somecommand --message="$message" args

Это имеет то преимущество, что $message можно использовать точно как строковая константа без лишних пробелов или разрывов строк.

Обратите внимание, что фактические строки сообщений, приведенные выше, имеют префикс с символом tab, который разделяется самим документом (из-за использования <<-). В конце есть разрывы строк, которые затем заменяются на dd пробелами.

Обратите внимание также, что если вы не удаляете новые строки, они будут отображаться как есть, когда "$message" будет расширен. В некоторых случаях вы можете обходным путем, удалив двойные кавычки вокруг $message, но сообщение больше не будет единственным аргументом.

Ответ 5

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

Пример:

echo "continuation
of 
lines" | tr '\n' ' '

Или, если это определение переменной, новые строки автоматически преобразуются в пробелы. Таким образом, лишние пробелы, только если применимо.

x="continuation
of multiple
lines"
y="red|blue|
green|yellow"

echo $x # This will do as the converted space actually is meaningful
echo $y | tr -d ' ' # Stripping of space may be preferable in this case

Ответ 6

Это не совсем то, о чем просил пользователь, но еще один способ создать длинную строку, которая занимает несколько строк, - это постепенно наращивать ее, например, так:

$ greeting="Hello"
$ greeting="$greeting, World"
$ echo $greeting
Hello, World

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

Ответ 7

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

    echo "continuation \
    lines"
>continuation     lines

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

    echo 'continuation' \
    'lines'
>continuation lines

Примечание. Конкатенация включает пробел.

Ответ 8

В зависимости от того, какие риски вы принимаете и насколько хорошо вы знаете и доверяете данным, вы можете использовать упрощенную интерполяцию переменных.

$: x="
    this
    is
       variably indented
    stuff
   "
$: echo "$x" # preserves the newlines and spacing

    this
    is
       variably indented
    stuff

$: echo $x # no quotes, stacks it "neatly" with minimal spacing
this is variably indented stuff

Ответ 9

В некоторых сценариях может быть целесообразно использовать возможность конкатенации Bash.

Пример:

temp='this string is very long '
temp+='so I will separate it onto multiple lines'
echo $temp
this string is very long so I will separate it onto multiple lines

Из раздела ПАРАМЕТРЫ страницы Bash Man:

name=[value]...

... В контексте, где оператор присваивания присваивает значение переменной оболочки или индексу массива, оператор + = может использоваться для добавления или добавления к предыдущему значению переменной. Когда + = применяется к переменной, для которой был задан целочисленный атрибут, значение оценивается как арифметическое выражение и добавляется к текущему значению переменной, которое также оценивается. Когда + = применяется к переменной массива с использованием составного присваивания (см. массивы ниже), значение переменной не сбрасывается (как при использовании =), и новые значения добавляются в массив, начиная с единицы, большей индекса максимума массива (для индексированных массивов) или добавляются как дополнительные пары ключ-значение в ассоциативном массиве. При применении к строковой переменной значение раскрывается и добавляется к значению переменной.

Ответ 10

Это, вероятно, не отвечает на ваш вопрос, но в любом случае вы можете найти его полезным.

Первая команда создает script, который отображается второй командой.

Третья команда делает этот script исполняемый файл.

Четвертая команда предоставляет пример использования.

[email protected]:~/tmp/so$ echo $'#!/usr/bin/env python\nimport textwrap, sys\n\ndef bash_dedent(text):\n    """Dedent all but the first line in the passed `text`."""\n    try:\n        first, rest = text.split("\\n", 1)\n        return "\\n".join([first, textwrap.dedent(rest)])\n    except ValueError:\n        return text  # single-line string\n\nprint bash_dedent(sys.argv[1])'  > bash_dedent
[email protected]:~/tmp/so$ cat bash_dedent 
#!/usr/bin/env python
import textwrap, sys

def bash_dedent(text):
    """Dedent all but the first line in the passed `text`."""
    try:
        first, rest = text.split("\n", 1)
        return "\n".join([first, textwrap.dedent(rest)])
    except ValueError:
        return text  # single-line string

print bash_dedent(sys.argv[1])
[email protected]:~/tmp/so$ chmod a+x bash_dedent
[email protected]:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

Обратите внимание, что если вы действительно хотите использовать этот script, имеет смысл перемещать исполняемый файл script в ~/bin, чтобы он был на вашем пути.

Проверьте ссылку на python, чтобы узнать, как работает textwrap.dedent.

Если использование $'...' или "$(...)" запутывает вас, задайте другой вопрос (по одному для каждой конструкции), если его еще нет. Возможно, было бы неплохо предоставить ссылку на вопрос, который вы найдете/попросите, чтобы у других людей была ссылка.