ฉันจะมีการขึ้นบรรทัดใหม่ในสตริงเป็น sh ได้อย่างไร?


339

นี้

STR="Hello\nWorld"
echo $STR

ผลิตเป็นเอาท์พุท

Hello\nWorld

แทน

Hello
World

ฉันควรทำอย่างไรเพื่อให้มีการขึ้นบรรทัดใหม่ในสตริง

หมายเหตุ: คำถามนี้ไม่ได้เกี่ยวกับก้อง ฉันรู้echo -eแต่ฉันกำลังมองหาโซลูชันที่อนุญาตให้ส่งผ่านสตริง (ซึ่งรวมถึงการขึ้นบรรทัดใหม่) เป็นอาร์กิวเมนต์สำหรับคำสั่งอื่น ๆที่ไม่มีตัวเลือกที่คล้ายกันเพื่อตีความหมาย\nของการขึ้นบรรทัดใหม่

คำตอบ:


353

การแก้ปัญหาคือการใช้$'string'ตัวอย่างเช่น:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

นี่คือข้อความที่ตัดตอนมาจากหน้าคู่มือ Bash:

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.

74
นั่นคือทุบตีที่ถูกต้อง แต่ไม่ใช่ POSIX sh
jmanning2k

7
+ หมายเหตุเพียงอย่างเดียว: ส่งออก varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev

3
นี้ไม่แน่นอนการทำงานกับสตริงตัวอักษรเป็น OP ถาม แต่ล้มเหลวโดยเร็วเป็นตัวแปรที่จะเปลี่ยนตัว: เพียงแค่พิมพ์STR=$"Hello\nWorld" Hello\nWorld
ssc

3
@ssc เครื่องหมายคำพูดเดี่ยว, ไม่ใช่ double
miken32

7
@ssc ไม่มีตัวแปรในตัวอย่างของคุณ นอกจากนี้วิธีนี้ต้องใช้เครื่องหมายคำพูดเดี่ยว ในที่สุดหากต้องการรวมตัวแปรที่มีการสอดแทรกคุณจะต้องเชื่อมสตริงที่มีเครื่องหมายคำพูดคู่และเครื่องหมายคำพูดพิเศษเป็นคำ ๆ เช่นH="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"จะก้อง "สวัสดี" และ "โลก" โดยแยกบรรทัด
JDS

166

เสียงก้องเป็นยุคและเต็มไปด้วยอันตรายที่ใช้จะส่งผลให้ทิ้งแกนไม่น้อยกว่า 4GB ปัญหาที่เกิดขึ้นอย่างจริงจังนั้นเป็นสาเหตุที่ทำให้กระบวนการ Unix Standardization ในที่สุดได้คิดค้นprintfยูทิลิตี้ขึ้นมาเพื่อแก้ไขปัญหาทั้งหมด

ดังนั้นเพื่อให้ได้บรรทัดใหม่ในสตริง:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

มี! ไม่มี SYSV เทียบกับ BSD echo madness ทุกอย่างได้รับการพิมพ์อย่างประณีตและรองรับการพกพาอย่างเต็มที่สำหรับลำดับ C escape ทุกคนได้โปรดใช้printfตอนนี้และอย่ามองย้อนกลับไป


3
ขอบคุณสำหรับคำตอบนี้ฉันคิดว่ามันให้คำแนะนำที่ดีจริงๆ อย่างไรก็ตามโปรดทราบว่าคำถามเดิมของฉันไม่ได้เกี่ยวกับวิธีการ echo เพื่อพิมพ์บรรทัดใหม่ แต่วิธีการขึ้นบรรทัดใหม่ให้เป็นตัวแปรเชลล์ คุณสามารถแสดงวิธีการทำที่ใช้ printf เช่นกัน แต่ผมคิดว่าการแก้ปัญหาจากamphetamachineใช้$'string'แม้จะทำความสะอาด
Juan A. Navarro

11
เฉพาะคำจำกัดความของ 'สะอาด' เท่านั้น $'foo'ไวยากรณ์ไม่ไวยากรณ์ POSIX ที่ถูกต้องเป็นปี 2013 เปลือกหอยหลายคนจะบ่น
เจนส์

5
BAR=$(printf "hello\nworld\n")ไม่พิมพ์ส่วนต่อท้าย \ n สำหรับฉัน
จอนนี่

3
@ จอนนี่มันไม่ควร; ข้อมูลจำเพาะของเชลล์สำหรับการทดแทนคำสั่งระบุว่ามีการขึ้นบรรทัดใหม่อย่างน้อยหนึ่งบรรทัดที่ท้ายเอาต์พุต ฉันรู้ว่านี่เป็นสิ่งที่ไม่คาดคิดสำหรับตัวอย่างของฉันและได้แก้ไขเพื่อรวมบรรทัดใหม่ใน printf
Jens

5
@ismael ไม่$()ถูกระบุโดย POSIX เป็นลำดับลบหนึ่งหรือมากกว่า <newline> ตัวละครในตอนท้ายของการทดแทน
Jens

61

สิ่งที่ฉันทำตามคำตอบอื่น ๆ คือ

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight

30

ปัญหาไม่ได้เกิดจากเปลือก ปัญหาคือจริงด้วยechoคำสั่งตัวเองและการขาดเครื่องหมายคำพูดคู่รอบการแก้ไขตัวแปร คุณสามารถลองใช้echo -eแต่ไม่สามารถใช้ได้กับทุกแพลตฟอร์มและหนึ่งในเหตุผลprintfแนะนำให้พกพาได้

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

#!/bin/sh
echo "Hello
World"
#EOF

หรือเทียบเท่า

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!

24

ฉันพบว่า-eธงสวยงามและตรงไปตรงมา

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

หากสตริงเป็นผลลัพธ์ของคำสั่งอื่นฉันก็ใช้เครื่องหมายคำพูด

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"

2
เรื่องนี้ถูกกล่าวถึงแล้วในคำตอบอื่น ๆ นอกจากนี้แม้ว่ามันจะมีอยู่แล้วผมได้แก้ไขเพียงคำถามที่จะเน้นความจริงที่ว่าคำถามนี้ไม่ได้echoเกี่ยวกับ
Juan A. Navarro

นี่ไม่ใช่คำตอบสำหรับคำถาม
Rohit Gupta

ทั้งหมดนี้ตอบคำถามที่ฉันกำลังมองหา! ขอบคุณ!
Kieveli

1
ทำงานได้ดีสำหรับฉันที่นี่ ฉันเคยสร้างไฟล์ตามเสียงสะท้อนนี้และสร้างบรรทัดใหม่บนเอาต์พุตอย่างถูกต้อง
Mateus Leon

echo -eมีชื่อเสียงในเรื่องของการพกพา นี้จะน้อยจากสถานการณ์ป่าตะวันตกในขณะนี้กว่าก่อน POSIX echo -eแต่ยังคงมีเหตุผลที่จะไม่ต้องการ ดูstackoverflow.com/questions/11530203/…
tripleee

21
  1. ทางเลือกง่าย ๆ เพียงอย่างเดียวคือการพิมพ์บรรทัดใหม่ในตัวแปร:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line

    ใช่นั่นหมายถึงการเขียนEnterที่จำเป็นในรหัส

  2. มีnew lineอักขระหลายตัวที่เทียบเท่ากัน

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.

    แต่สิ่งเหล่านั้นต้องการ "การตีความ" โดยเครื่องมือบางอย่าง ( POSIX printf ):

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    ดังนั้นเครื่องมือจำเป็นต้องสร้างสตริงด้วยบรรทัดใหม่:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. ในบางเชลล์ลำดับ$'คือการขยายเชลล์พิเศษ เป็นที่รู้จักในการทำงานใน ksh93 ทุบตีและ zsh:

    $ STR=$'new\nline'
  4. แน่นอนว่ายังสามารถแก้ปัญหาที่ซับซ้อนได้อีกด้วย:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    หรือ

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

6

$ ขวาก่อนเครื่องหมายคำพูดเดี่ยว '... \ n ... ' ดังต่อไปนี้ แต่เครื่องหมายคำพูดคู่ไม่ทำงาน

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld

4

ฉันไม่มีผู้เชี่ยวชาญทุบตี แต่สิ่งนี้เหมาะสำหรับฉัน:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

ฉันพบว่าการจัดรูปแบบข้อความนี้ง่ายขึ้น


2
เก่าดีheredoc และก็อาจเป็นไปตาม POSIX เช่นกัน!
pyb

คำพูดรอบ$NEWSTRในecho "$NEWSTR"มีความสำคัญที่นี่
Shadi

0

ในระบบของฉัน (Ubuntu 17.10) ตัวอย่างของคุณใช้งานได้ตามต้องการทั้งเมื่อพิมพ์จากบรรทัดคำสั่ง (เป็นsh) และเมื่อเรียกใช้งานเป็นshสคริปต์:

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

ฉันเดาว่าสิ่งนี้จะตอบคำถามของคุณ: ใช้งานได้ (ฉันไม่ได้พยายามหารายละเอียดเช่นในเวลาใดการเปลี่ยนตัวอักขระ\nขึ้นบรรทัดใหม่ที่เกิดขึ้นsh)

อย่างไรก็ตามฉันสังเกตเห็นว่าสคริปต์เดียวกันนี้จะทำงานแตกต่างกันเมื่อทำงานด้วยbashและจะพิมพ์ออกมาHello\nWorldแทน:

[bash bash test-str.sh
Hello\nWorld

ฉันจัดการเพื่อให้ได้ผลลัพธ์ที่ต้องการด้วยbashดังนี้:

[bash STR="Hello
> World"
[bash echo "$STR"

$STRหมายเหตุคำพูดสองรอบ สิ่งนี้จะทำงานเหมือนกันหากบันทึกและเรียกใช้เป็นbashสคริปต์

ต่อไปนี้ยังให้ผลลัพธ์ที่ต้องการ:

[bash echo "Hello
> World"

0

คนที่จู้จี้จุกจิกที่ต้องการเพียงการขึ้นบรรทัดใหม่และดูหมิ่นรหัส multiline ที่ทำให้การเยื้องสามารถทำได้:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

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

IFS="$(printf '\nx')" IFS="${IFS%x}"

ฉันรู้ว่านี่เป็นสองการกระทำแทนหนึ่ง แต่เยื้องและพกพาของฉัน OCD เป็นที่ความสงบสุขในขณะนี้ :) ฉันสร้างสรรค์พัฒนานี้จะสามารถที่จะแยกการขึ้นบรรทัดใหม่เท่านั้นแยกออกและฉันสิ้นสุดขึ้นโดยใช้การปรับเปลี่ยนที่ใช้\rเป็นตัวอักษรยุติ \r\nที่ทำให้การทำงานขึ้นบรรทัดใหม่แยกแม้สำหรับดอสเอาท์พุทที่ลงท้ายด้วย

IFS="$(printf '\n\r')"

0

ฉันไม่พอใจกับตัวเลือกใด ๆ ที่นี่ นี่คือสิ่งที่ได้ผลสำหรับฉัน

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")

นั่นเป็นวิธีที่น่าเบื่อมากในการเขียนstr=$(printf '%s\n' "first line" "another line" "and another line")(เชลล์จะสะดวก (หรือไม่) ตัดแต่งบรรทัดใหม่สุดท้ายจากการแทนที่คำสั่ง) หลายคำตอบที่นี่ส่งเสริมprintfในรูปแบบต่าง ๆ ดังนั้นฉันไม่แน่ใจว่าสิ่งนี้จะเพิ่มมูลค่าใด ๆ เป็นคำตอบที่แยกต่างหาก
tripleee

มันเป็นสตริงสั้น ๆ แต่ถ้าคุณต้องการเก็บสิ่งต่าง ๆ ให้เป็นระเบียบและแต่ละบรรทัดมีความยาว 60 ตัวอักษรมันเป็นวิธีที่เป็นระเบียบเรียบร้อยในการทำมัน ระบุว่ามันเป็นทางออกที่ฉันไปด้วยในที่สุดมันจะเพิ่มบางสิ่งบางอย่างถ้าไม่ใช่สำหรับคุณแล้วสำหรับตัวเองในอนาคตเมื่อฉันกลับมาอย่างไม่ต้องสงสัย
Adam K Dean

1
แม้สำหรับสายยาวฉันชอบprintf '%s\n' "first line" \ (ขึ้นบรรทัดใหม่เยื้อง) 'second line'ฯลฯ ดูเพิ่มเติมprintf -v strสำหรับการกำหนดให้กับตัวแปรโดยไม่ต้องวางไข่ subshell
tripleee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.