ทุบตีสายต่อเนื่อง


158

คุณจะใช้สายต่อเนื่อง bash ได้อย่างไร?

ฉันรู้ว่าคุณสามารถทำได้:

echo "continuation \
lines"
>continuation lines

อย่างไรก็ตามหากคุณมีรหัสที่เยื้องมันก็ไม่ได้ผลดีนัก:

    echo "continuation \
    lines"
>continuation     lines

5
คู่มือ Bash Shell Style ของ Google แนะนำเอกสาร "ที่นี่"สำหรับ "หากคุณต้องเขียนสตริงที่ยาวเกิน 80 อักขระ" ดู@ คำตอบของ
เทรเวอร์บอยด์สมิ ธ

google.github.io/styleguide/ ...... - นี่คือลิงค์โดยตรงในเอกสารใหม่
jcollum

คำตอบ:


161

นี่คือสิ่งที่คุณอาจต้องการ

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

หากสิ่งนี้สร้างสองอาร์กิวเมนต์เพื่อ echo และคุณต้องการเพียงหนึ่งแล้วลองดูที่การต่อสตริง ในทุบตีวางสองสายติดกันเรียงกัน:

$ 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
โอ้คุณต้องการสตริง (ทุบตี) เส้นเดียวเพื่อขยายบรรทัด! ฉันเห็นแล้ว
Ray Toal

4
โซลูชันที่มีตัวแปรเดียว:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman

30

ที่นี่เอกสารที่มีตัว<<-HEREยุติทำงานได้ดีสำหรับสตริงข้อความแบบหลายบรรทัดที่เยื้อง มันจะลบแท็บนำออกจากเอกสาร 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)"

นอกจากนี้ยังทำงานได้ดีในบริบทที่คุณต้องการฝังชิ้นขี้ปะติ๋วของเชลล์สคริปต์ในภาษาอื่นที่ไวยากรณ์ภาษาโฮสต์จะไม่ยอมให้คุณใช้เอกสารที่นี่เช่นในหรือMakefileDockerfile

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ตัวอย่างแรกด้วยตอนนี้)
tripleee

12

คุณสามารถใช้bash arrays

$ str_array=("continuation"
             "lines")

แล้วก็

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

มีพื้นที่เพิ่มเติมเนื่องจาก (หลังจาก bash manual):

ถ้าคำนี้ถูกยกมาสองคำให้${name[*]}ขยายเป็นคำเดียวโดยมีค่าของสมาชิกอาเรย์แต่ละตัวคั่นด้วยอักขระตัวแรกของตัวแปร IFS

ดังนั้นตั้งIFS=''เพื่อกำจัดพื้นที่พิเศษ

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

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:

ชื่อ = [ค่า] ...

... ในบริบทที่คำสั่งการมอบหมายกำลังกำหนดค่าให้กับตัวแปรเชลล์หรือดัชนีอาร์เรย์คุณสามารถใช้เครื่องหมาย + = เพื่อผนวกหรือเพิ่มค่าก่อนหน้าของตัวแปร เมื่อ + = ถูกนำไปใช้กับตัวแปรที่มีการตั้งค่าแอตทริบิวต์จำนวนเต็มค่าจะถูกประเมินเป็นนิพจน์ทางคณิตศาสตร์และเพิ่มลงในค่าปัจจุบันของตัวแปรซึ่งจะถูกประเมินเช่นกัน เมื่อ + = ถูกนำไปใช้กับตัวแปรอาร์เรย์โดยใช้การมอบหมายแบบผสม (ดูอาร์เรย์ด้านล่าง) ค่าของตัวแปรจะไม่ถูกยกเลิก (เช่นเมื่อใช้ =) และค่าใหม่จะถูกผนวกเข้ากับอาร์เรย์ที่เริ่มต้นที่หนึ่งที่สูงกว่าดัชนีสูงสุดของอาร์เรย์ (สำหรับอาร์เรย์ที่ทำดัชนี) หรือเพิ่มเป็นคู่ของคีย์ - ค่าเพิ่มเติมในอาเรย์แบบเชื่อมโยง เมื่อนำไปใช้กับตัวแปรที่มีค่าสตริงค่าจะถูกขยายและผนวกเข้ากับค่าของตัวแปร


อาจเป็นคำแนะนำที่ดีที่สุดในหน้านี้ การใช้ HEREDOC และการไพพ์ลงในการแปลสำหรับ <CR> นั้นไม่ได้ใช้งานง่ายและล้มเหลวเมื่อสตริงต้องมีตัวคั่นบรรทัดที่แยกกัน
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.

Voila!


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 arguments แต่มันไม่ทำงานกับสิ่งอื่นเช่นการกำหนดตัวแปร แม้ว่าคำถามจะไม่เกี่ยวกับตัวแปร แต่การใช้ 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นั้นเหมาะสมกว่าเพื่อให้มันอยู่ในเส้นทางของคุณ

ตรวจสอบการอ้างอิงหลามสำหรับรายละเอียดเกี่ยวกับวิธีการtextwrap.dedentทำงาน

หากการใช้งาน$'...'หรือ"$(...)"สร้างความสับสนให้กับคุณให้ถามคำถามอื่น (หนึ่งข้อต่อการสร้าง) หากยังไม่ได้สร้างขึ้น มันอาจจะดีที่จะให้ลิงค์ไปยังคำถามที่คุณพบ / ถามเพื่อให้คนอื่นจะมีการอ้างอิงที่เชื่อมโยง


6
เจตนาดีและอาจมีประโยชน์แม้ว่าอาจเป็นไปได้ OP ขอคำแนะนำเกี่ยวกับไวยากรณ์การทุบตีพื้นฐานและคุณให้คำนิยามฟังก์ชันหลามซึ่งใช้กระบวนทัศน์ OO การควบคุมการไหลพิเศษและการนำเข้า นอกจากนี้คุณเรียกว่าไฟล์เอ็กซีคิวต์ซึ่งเป็นส่วนหนึ่งของการแก้ไขสตริงซึ่งเป็นสิ่งที่บุคคลที่ถามคำถามแบบนี้จะยังไม่ได้เห็นอย่างแน่นอน
คู่ปรับ Shot
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.