สตริงหลายบรรทัดพร้อมพื้นที่เพิ่มเติม (เยื้องที่สงวนไว้)


409

ฉันต้องการที่จะเขียนข้อความที่กำหนดไว้ล่วงหน้าบางส่วนลงในไฟล์ที่มีต่อไปนี้:

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อัน แต่พื้นที่พิเศษจะออกมาได้อย่างไร


2
ฉันไม่แน่ใจ แต่ .. ถ้าคุณแค่พิมพ์text="this is line one\nthis is line two\nthis is line three"ในบรรทัดเดียวกัน .. (ไม่มีการป้อนข้อมูลใด ๆ )
Yohanes Khosiawan 许先汉

4
ลบ\nในแต่ละบรรทัดคุณได้กดขึ้นบรรทัดใหม่เพื่อย้ายไปยังบรรทัดใหม่
Mark Setchell

คุณได้รับแล้ว\nทำไมคุณใส่บรรทัดใหม่ในบรรทัดใหม่ เพียงแค่text="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi

1
การเอาส่วน\nท้ายของแต่ละบรรทัดออกทำให้การส่งออกทั้งหมดทำงานร่วมกันในบรรทัดเดียว
Jonathan Hartley

18
Aha: การใส่เครื่องหมายคำพูดคู่ล้อมรอบ"$text"เส้นเสียงนั้นเป็นสิ่งสำคัญ หากไม่มีพวกเขาจะไม่มีการขึ้นบรรทัดใหม่ (ทั้งตัวอักษรและ '\ n') กับพวกเขาพวกเขาทั้งหมดทำ
Jonathan Hartley

คำตอบ:


662

Heredoc ฟังดูสะดวกกว่าสำหรับจุดประสงค์นี้ มันถูกใช้เพื่อส่งหลายคำสั่งไปยังโปรแกรมตัวแปลคำสั่งเช่นexหรือcat

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

สตริงหลังจาก<<ระบุตำแหน่งที่จะหยุด

ในการส่งบรรทัดเหล่านี้ไปยังไฟล์ให้ใช้:

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

คุณสามารถจัดเก็บบรรทัดเหล่านี้ไปยังตัวแปรได้:

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

VARนี้จะจัดเก็บสายให้กับตัวแปรที่มีชื่อ

เมื่อพิมพ์ให้จำเครื่องหมายคำพูดรอบ ๆ ตัวแปรไม่เช่นนั้นคุณจะไม่เห็นอักขระขึ้นบรรทัดใหม่

echo "$VAR"

ยิ่งไปกว่านั้นคุณสามารถใช้การเยื้องเพื่อทำให้โดดเด่นยิ่งขึ้นในรหัสของคุณ เวลานี้เพียงเพิ่ม-หลังจาก<<เพื่อหยุดแท็บไม่ให้ปรากฏ

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

แต่คุณต้องใช้แท็บไม่ใช่เว้นวรรคเพื่อเยื้องรหัสของคุณ


38
ทำไมคุณถึงมี << - แทนที่จะเป็น << ในบรรทัดแรกของบล็อคโค้ดที่ 2 และ 5? ทำอะไรเป็นพิเศษหรือไม่
lohfu

35
@ sup3rman '-' ละเว้นแท็บนำ ดูstackoverflow.com/questions/2500436/…
kervin

7
ฉันจะอ่านออกด้วยสถานะ 0 ได้อย่างไร ดูเหมือนว่าไม่พบจุดสิ้นสุดของบรรทัด (เนื่องจากเป็น -d '') และออกด้วย 1 ซึ่งไม่ได้ช่วยเมื่อคุณอยู่ในสคริปต์
Misha Slyusarev

6
@MishaSlyusarev ใช้ -d '\ 0' และเพิ่ม \ 0 ที่ส่วนท้ายของบรรทัดสุดท้ายของคุณ
Lars Schneider

11
การใช้-dตัวเลือกในread -r -d '' VARIABLE <<- EOMไม่ได้ผลสำหรับฉัน
dron22

186

หากคุณพยายามที่จะทำให้สตริงเป็นตัวแปรวิธีที่ง่ายอีกอย่างหนึ่งก็คือ

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

หากคุณเยื้องสตริงด้วยแท็บ (เช่น '\ t') การเยื้องจะถูกตัดออก หากคุณเยื้องด้วยช่องว่างการเยื้องจะถูกทิ้งไว้

หมายเหตุ: มันเป็นสิ่งสำคัญที่วงเล็บปิดล่าสุดอยู่ในอีกบรรทัดหนึ่ง ENDข้อความต้องปรากฏบนบรรทัดด้วยตัวเอง


1
นั่นสำคัญหรือไม่? ทำงานบน mac ของฉันด้วย)ในบรรทัดเดียวกัน ฉันคิดว่าเป็นเพราะสิ่งที่เกิดขึ้นระหว่าง$(และ)จะดำเนินการในจักรวาลของตัวเองและคำสั่งจริงจะไม่เห็น ')' อย่างไรก็ตาม ฉันสงสัยว่ามันใช้ได้กับคนอื่นด้วยหรือไม่
deej

เป็นไปได้ว่ากระสุนที่แตกต่างกันจะตีความสิ่งต่างกัน ในเอกสารประกอบการทุบตีดูเหมือนจะไม่พูดอะไรทางเดียวหรืออื่น ๆ แต่ตัวอย่างทั้งหมดมีมันในสายของตัวเอง
Andrew Miner

สิ่งที่สนุกคือฉันพบว่าคุณตอบเมื่อพยายามกำหนดค่าสำหรับตัวแปร USAGE ด้วย ดังนั้นคำตอบของคุณจึงตรงกัน :)
Bunyk

1
@deej จากข้อมูลจำเพาะของ POSIX : เอกสารที่นี่ ... ดำเนินต่อไปจนกว่าจะมีบรรทัดที่มีเพียงตัวคั่นและ <newline>
Fornost

1
@Fornost มันไม่ได้ขัดแย้งกับสิ่งที่ฉันพูด
deej

84

echoเพิ่มช่องว่างระหว่างข้อโต้แย้งที่ส่งผ่านไป $textขึ้นอยู่กับการขยายตัวของตัวแปรและการแยกคำดังนั้นechoคำสั่งของคุณจะเทียบเท่ากับ:

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

คุณจะเห็นว่าจะมีการเพิ่มช่องว่างก่อน "นี่" คุณสามารถลบอักขระบรรทัดใหม่และอ้างอิง$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

1
ขอบคุณสำหรับการอธิบายว่าทำไม mod เห็นพื้นที่พิเศษเมื่อใช้คำสั่ง“ echo” คำตอบนี้ยังทำงานได้ดีเมื่อใช้ในสคริปต์ทุบตี (ตรงกันข้ามกับเซสชันเชลล์แบบโต้ตอบ) รู้สึกว่านี่เป็นคำตอบที่แท้จริงและควรเป็นคำตอบที่ยอมรับได้
onelaview

1
คำตอบนี้ควรได้รับการยอมรับ คำตอบอื่น ๆ ทั้งหมดให้วิธีที่น่าสนใจมากมายในการแก้ปัญหาความเจ็บปวดของ OP แต่ในทางเทคนิคคำถามก็คือ "พื้นที่พิเศษออกมาเป็นอย่างไร" และไม่มีใครพูดถึงเรื่องนี้ :-) !!! ขอบคุณ Josh ที่ทำให้ความลึกลับ -1!
Dmitry Shevkoplyas

45

ในสคริปต์ทุบตีงานต่อไปนี้:

#!/bin/sh

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

อีกทางเลือกหนึ่ง:

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

ชื่อไฟล์แมวให้:

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

ดูเหมือนว่ากลวิธีทางเลือกจะใช้งานได้ในขณะที่ Shell Scripts แต่ดูเหมือนว่าจะไม่ได้ผลสำหรับการทุบตี
Macindows

3
@Macindows ฉันดูเหมือนจะลืมเลือกสำหรับ-e echoกรุณาลองอีกครั้ง.
Chris Maes

41

ฉันพบวิธีแก้ไขปัญหาเพิ่มเติมเนื่องจากฉันต้องการให้มีการเยื้องทุกบรรทัดอย่างถูกต้อง:

  1. คุณสามารถใช้echo:

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename

    มันไม่ทำงานหากคุณใส่"\n"ก่อนหน้านี้\ในตอนท้ายของบรรทัด

  2. อีกวิธีหนึ่งคุณสามารถใช้printfเพื่อการพกพาที่ดีขึ้น (ฉันมีปัญหามากมายecho):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. อีกวิธีหนึ่งอาจจะ:

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this is line three\n"
    printf "%b" "$text" > filename

    หรือ

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
  4. ทางออกก็คือความสำเร็จโดยการผสมและprintfsed

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi

    มันไม่ง่ายเลยที่จะทำการ refactor code ในรูปแบบนี้ในขณะที่คุณ hardcode ระดับการเยื้องเข้าไปในโค้ด

  5. เป็นไปได้ที่จะใช้ฟังก์ชั่นตัวช่วยและเทคนิคการแทนที่ตัวแปร:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"

3b เป็นโซลูชันที่ฉันต้องการเมื่อต้องการรักษาการเยื้องรหัสและการเยื้องสตริงอย่างอิสระอย่างน้อยเมื่อฉันไม่สามารถใช้ 2 ในการกำหนดตัวแปรและสามารถอ่านได้มากกว่า 3a เมื่อฉันไม่สนใจฉันก็แค่เปิดข้อความที่ยกมาให้เปิดหลายบรรทัด
Pysis

คำตอบที่ดีมากโดยเฉพาะอย่างยิ่ง 3b
Sumit Trehan

"ไม่สามารถใช้งานได้หากคุณใส่" \ n "ก่อน \ บนจุดสิ้นสุดของบรรทัด" - เป็นคำแนะนำที่ดีเมื่อใช้เสียงสะท้อน
Noam Manos

6

ต่อไปนี้เป็นวิธีที่ฉันต้องการกำหนดสตริงหลายบรรทัดให้กับตัวแปร (ฉันคิดว่ามันดูดี)

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

จำนวนของการขีดล่างเหมือนกัน (ที่นี่ 80) ในทั้งสองกรณี


0

เพียงพูดถึงการต่อข้อมูลบรรทัดเดียวอย่างง่ายเนื่องจากบางครั้งก็มีประโยชน์

# for bash

v=" guga "$'\n'"   puga "

# Just for an example.
v2="bar "$'\n'"   foo "$'\n'"$v"

# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar ${n}   foo ${n}$v"

echo "$v3" 

คุณจะได้รับสิ่งนี้

บาร์ 
   foo 
 guga 
   Puga 

ช่องว่างสีขาวที่นำหน้าและลงท้ายด้วยทั้งหมดจะถูกสงวนไว้สำหรับ

echo "$v3" > filename

0

มีหลายวิธีที่จะทำ สำหรับฉันแล้วการสอดสายสตริงที่เยื้องเข้าไปในsedทำงานได้ดี

printf_strip_indent() {
   printf "%s" "$1" | sed "s/^\s*//g" 
}

printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"

คำตอบนี้ขึ้นอยู่กับคำตอบของMateusz Piotrowskiแต่ปรับปรุงเล็กน้อย


-1

มันจะทำงานถ้าคุณใส่มันดังต่อไปนี้:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.