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

158

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

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

echo "continuation \
lines"
>continuation lines

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

    echo "continuation \
    lines"
>continuation     lines
PyRulez
источник
5
Руководство по стилю Google Bash Shell рекомендует «здесь» документы для «Если вам нужно написать строки длиной более 80 символов». Смотрите ответ @ tripleee .
Тревор Бойд Смит
google.github.io/styleguide/… - это прямая ссылка в новом документе
jcollum

Ответы:

161

Это то, что вы можете хотеть

$       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

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

Рэй Тоал
источник
1
Спасибо за вашу помощь, но хотя это и удаляет пробелы, они теперь являются отдельными параметрами (Bash интерпретирует пробелы во второй строке как разделитель параметров) и теперь выводятся правильно только из-за команды echo.
1
О, вы бы хотели, чтобы одна (bash) строка занимала строки! Я вижу сейчас.
Рэй Тоал
4
Решение с одной переменной:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Джонни Тандерман
30

Здесь документы с <<-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 " \
    "doesn't specify any newline)"

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

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript
tripleee
источник
Не работает для меня Ubuntu 16.04. Я получаю две строки вместо ожидаемой сцепленной одной строки.
Пенге Генг
@PengheGeng Действительно, это решает проблему избавления от отступа, а не соединения линий. Вы все еще можете поставить обратную косую черту в конце строки, чтобы соединить две строки вместе.
tripleee
(Но см. Также первый printfпример.)
tripleee
12

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

$ str_array=("continuation"
             "lines")

затем

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

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

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

Так что, IFS=''чтобы избавиться от лишнего пространства

$ IFS=''
$ echo "${str_array[*]}"
continuationlines
tworec
источник
4

В определенных сценариях использование способности конкатенации 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:

имя = [значение] ...

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

Cybernaut
источник
Наверное, лучший совет на этой странице. Использование HEREDOC и передача в перевод для <CR> является сверхинтуитивным и завершается неудачно, когда в строке действительно нужно иметь дискретные разделители строк.
ingyhere
2

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

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

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

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

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

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

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

haridsv
источник
2

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

В случае echo:

# echo '-n' flag prevents trailing <CR> 
echo -n "This is my one-line statement" ;
echo -n " that I would like to make."
This is my one-line statement that I would like to make.

В случае с Vars:

outp="This is my one-line statement" ; 
outp+=" that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Другой подход в случае Vars:

outp="This is my one-line statement" ; 
outp="${outp} that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Вуаля!

ingyhere
источник
1

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

Пример:

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
rjni
источник
1

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

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

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

Джон МакКлюнг
источник
0

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

    echo "continuation \
    lines"
>continuation     lines

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

    echo 'continuation' \
    'lines'
>continuation lines

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

mMontu
источник
2
Он работает с аргументами echo и string, но не работает с другими вещами, такими как присвоение переменных. Хотя вопрос был не о переменных, использование echo было только примером. Вместо того , echo если у вас x=, вы получите ошибку: lines: command not found.
LS
0

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

$: 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
Пол Ходжес
источник
-2

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

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

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

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

john@malkovich:~/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
john@malkovich:~/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])
john@malkovich:~/tmp/so$ chmod a+x bash_dedent
john@malkovich:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

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

Проверьте Python ссылку для деталей о том, как textwrap.dedentработает.

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

созерцаемое
источник
6
С благими намерениями - и, возможно, даже полезными - хотя это может быть, OP попросил совета по базовому синтаксису bash, и вы дали ему определение функции python, которое использует парадигмы OO, исключительное управление потоком и импорт. Кроме того, вы назвали исполняемый файл как часть строковой интерполяции - то, что человек, задающий подобные вопросы, наверняка еще не видел в bash.
Парфянский выстрел