Лінії продовження Баша


158

Як ви використовуєте лінії продовження bash?

Я розумію, що ти можеш це зробити:

echo "continuation \
lines"
>continuation lines

Однак якщо у вас є відступ коду, він не виходить так добре:

    echo "continuation \
    lines"
>continuation     lines

5
Посібник зі стилю Bash Shell від Google рекомендує "тут" документи для "Якщо вам потрібно написати рядки, що перевищують 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) рядок простягалася лініями! Я зараз бачу.
Ray Toal

4
Рішення з однією змінною:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman

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

Не працює для мене. Ubuntu 16.04. Я отримую два рядки замість очікуваного з'єднаного одного рядка.
Penghe Geng

@PengheGeng Дійсно, це вирішує проблему позбавлення відступу, а не того, як з'єднувати лінії разом. Ви все ще можете нахилити новий рядок у кінці рядка, щоб з'єднати два рядки разом.
tripleee

(Але дивіться перший printfприклад також і зараз.)
триплея

12

Можна використовувати масиви bash

$ str_array=("continuation"
             "lines")

тоді

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

є додаткове місце, тому що (після інструкції з bash):

Якщо слово подвійне цитування, ${name[*]}розширюється на одне слово зі значенням кожного члена масиву, розділеного першим символом змінної IFS

Тож налаштуйтеся IFS=''позбутися зайвого місця

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

4

У певних сценаріях використання можливостей злучення Баша може бути доречним.

Приклад:

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

З розділу PARAMETERS на сторінці Bash Man:

name = [значення] ...

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


Напевно, найкраща порада на цій сторінці. Використання HEREDOC та передача даних у перекладі для <CR> s є надто неінтуїтивним та не вдається, коли рядок насправді повинен мати дискретно розміщені розділові лінії.
ingyhere

2

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

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, але повідомлення більше не буде єдиним аргументом.


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.

Вуаля!


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

1

Користувач не точно запитав про це, але інший спосіб створити довгу рядок, що охоплює декілька рядків, - поступово нарощуючи її так:

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

Очевидно, що в цьому випадку було б простіше побудувати його за один раз, але цей стиль може бути дуже легким і зрозумілим при роботі з довшими струнами.


0

Однак якщо у вас є відступ коду, він не виходить так добре:

    echo "continuation \
    lines"
>continuation     lines

Спробуйте з окремими цитатами та об'єднати рядки:

    echo 'continuation' \
    'lines'
>continuation lines

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


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
Хороше цілеспрямоване і, можливо, навіть корисне, хоча це може бути, ОП попросило поради щодо базового синтаксису bash, і ви дали йому визначення функції python, яке використовує парадигми ОО, надзвичайний контроль потоку та імпорт. Крім того, ви назвали виконуваний файл як частину рядкової інтерполяції - те, чого людина, яка задає подібні запитання, точно не побачила б у башті.
Парфянський розстріл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.