วิธีลบการขึ้นบรรทัดใหม่จากสตริงใน Bash


110

ฉันมีตัวแปรต่อไปนี้

echo "|$COMMAND|"

ซึ่งส่งคืน

|
REBOOT|

ฉันจะลบบรรทัดแรกนั้นได้อย่างไร


7
เหตุใดจึงถูกทำเครื่องหมายว่าเป็นการหลอกลวง คำถามนี้เกี่ยวกับการลบจุดกลับแคร่ คำถามซ้ำซ้อนที่ถูกกล่าวหาว่าเชื่อมโยงไปถึงเกี่ยวกับการลบช่องว่างและกล่าวถึงเฉพาะการคืนค่าขนส่งเท่านั้น
Michael Scheper

1
@MichaelScheper เห็นด้วยและเปิดใหม่
fedorqui 'SO หยุดทำร้าย'

คำศัพท์ที่นี่เป็นคำที่แปลก อักขระ Unix line-termination \n(hex 0x0A) เรียกว่า Lie feed (LF); มันแตกต่างจากการส่งคืนอักขระ (CR) \r(ฐานสิบหก 0x0D) ซึ่งใช้บน Windows และในโปรโตคอลเครือข่ายจำนวนมากเป็นไบต์แรกใน CR LF ลำดับบรรทัดที่สิ้นสุดแบบสองไบต์
tripleee

คำตอบ:


115

ภายใต้ มีการทุบตีบางอย่าง:

trคำสั่งจะถูกแทนที่ด้วย// bashism :

COMMAND=$'\nREBOOT\r   \n'
echo "|${COMMAND}|"
|
   OOT
|

echo "|${COMMAND//[$'\t\r\n']}|"
|REBOOT   |

echo "|${COMMAND//[$'\t\r\n ']}|"
|REBOOT|

ดูการขยายพารามิเตอร์และการเสนอราคาในหน้า man ของ bash:

man -Pless\ +/\/pattern bash
man -Pless\ +/\\\'string\\\' bash

man -Pless\ +/^\\\ *Parameter\\\ Exp bash
man -Pless\ +/^\\\ *QUOTING bash

ต่อไป ...

ตามที่ @AlexJordan ถามสิ่งนี้จะระงับอักขระที่ระบุทั้งหมด แล้วถ้า$COMMANDมีช่องว่าง ...

COMMAND=$'         \n        RE BOOT      \r           \n'
echo "|$COMMAND|"
|
           BOOT      
|

CLEANED=${COMMAND//[$'\t\r\n']}
echo "|$CLEANED|"
|                 RE BOOT                 |

shopt -q extglob || { echo "Set shell option 'extglob' on.";shopt -s extglob;}

CLEANED=${CLEANED%%*( )}
echo "|$CLEANED|"
|                 RE BOOT|

CLEANED=${CLEANED##*( )}
echo "|$CLEANED|"
|RE BOOT|

ไม่นาน:

COMMAND=$'         \n        RE BOOT      \r           \n'
CLEANED=${COMMAND//[$'\t\r\n']} && CLEANED=${CLEANED%%*( )}
echo "|${CLEANED##*( )}|"
|RE BOOT|

บันทึก: มีextglobตัวเลือกที่จะเปิดใช้งาน ( shopt -s extglob) เพื่อใช้*(...)ไวยากรณ์


1
นี่คือวิธีการดึงเส้นฟีดออกจากตัวแปรใน BASH คำตอบอื่น ๆ คือการเรียกใช้กระบวนการ TR เพิ่มเติมโดยไม่จำเป็น BTW มีประโยชน์เพิ่มเติมในการลบช่องว่างสิ้นสุด!
นี่

1
โปรดทราบว่ามันยังลบช่องว่างภายในออกจากสตริงด้วยเช่นกัน ... COMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n ']}|"ส่งกลับ|REBOOT|
Alex Jordan

2
@AlexJordan ใช่มันเป็นคุณลักษณะที่ต้องการ : คุณสามารถ whipe พื้นที่หลังจาก\nการป้องกันนี้: จะกลับมาCOMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n']}|" |RE BOOT|
F. Hauri

2
รูปแบบการทำงานเป็น${COMMAND//[$'\t\r\n']}อย่างไร? ฉันคิดว่า${COMMAND//[\t\r\n]}จะได้ผล แต่มันก็ไม่เป็นเช่นนั้น $สัญลักษณ์ของอะไรและเครื่องหมายคำพูดเดี่ยวด้วย?
haridsv

1
เกี่ยวกับไวยากรณ์ $ '' นั่นคือANSI C quoting (กล่าวถึงในคำตอบ wjordans)
chuckx

81
echo "|$COMMAND|"|tr '\n' ' '

จะแทนที่บรรทัดใหม่ (ใน POSIX / Unix ไม่ใช่การกลับรถ) ด้วยช่องว่าง

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

อืมดูเหมือนว่ามันจะเป็นช่องโหว่ด้านความปลอดภัยที่น่ากลัวเช่นกันขึ้นอยู่กับว่าข้อมูลมาจากไหน


วันที่มาจากคำขอ curl ซึ่งอยู่บนเซิร์ฟเวอร์เดียวกันฉันจะใส่มันลงใน var ใหม่ได้อย่างไร newvar = $ (echo "| $ COMMAND |" | tr '\ n' '')
Matt Leyland

2
ใช่. แต่โปรดบอกฉันว่าคุณไม่อนุญาตให้คนทั่วไปรีบูตเซิร์ฟเวอร์ของคุณจากระยะไกลโดยไม่ใช้รหัสผ่าน ...
Robin Green

31
ทำไมคุณไม่ใช้tr -d '\n'สำหรับวางแทนการแทนที่ด้วยช่องว่าง
F. Hauri

3
กรุณาแส้ท่อที่ไร้ประโยชน์! ใช้tr '\n' ' ' <<< "|$COMMAND|"แทนecho ... | ...
F. Hauri

12
@ F.Hauri: หรือ tr ที่ไร้ประโยชน์:"|${COMMAND//$'\n'}|"
rici

76

ทำความสะอาดตัวแปรของคุณโดยลบการส่งคืนแคร่ทั้งหมด:

COMMAND=$(echo $COMMAND|tr -d '\n')

22
นั่นไม่ได้เป็นแถบฟีด? ไม่ควรเป็นtr -d '\r'แทน?
Izzy

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

ฉันได้อัปเดตคำถามเป็น "linefeed" แล้วตามตัวอย่างที่แสดงให้เห็นว่าเป็นตัวป้อนบรรทัดไม่ใช่การกลับรถ คำตอบนี้ยังคงถูกต้อง แต่ควรอัปเดตเป็น "linefeed" แทน "carriage return" หรือไม่
Benjamin W.

8

ใช้bash:

echo "|${COMMAND/$'\n'}|"

(โปรดสังเกตว่าอักขระควบคุมในคำถามนี้คือ 'newline' ( \n) ไม่ใช่ carriage return ( \r) ตัวหลังจะมีเอาต์พุตREBOOT|เป็นบรรทัดเดียว)

คำอธิบาย

ใช้การขยายพารามิเตอร์ Bash Shell${parameter/pattern/string} :

รูปแบบที่มีการขยายการผลิตรูปแบบเช่นเดียวกับการขยายตัวในชื่อไฟล์ พารามิเตอร์ที่มีการขยายตัวและการแข่งขันที่ยาวที่สุดของรูปแบบกับค่าของมันจะถูกแทนที่ด้วยสตริง [... ] ถ้าสตริงเป็นโมฆะการจับคู่ของรูปแบบจะถูกลบและรูปแบบ/ต่อไปนี้อาจถูกละไว้

นอกจากนี้ยังใช้$'' ANSI-C quoting$'\n'สร้างเพื่อระบุการขึ้นบรรทัดใหม่เป็น การใช้ newline โดยตรงก็ใช้ได้เช่นกันแม้ว่าจะไม่ค่อยสวยก็ตาม:

echo "|${COMMAND/
}|"

ตัวอย่างเต็ม

#!/bin/bash
COMMAND="$'\n'REBOOT"
echo "|${COMMAND/$'\n'}|"
# Outputs |REBOOT|

หรือใช้การขึ้นบรรทัดใหม่:

#!/bin/bash
COMMAND="
REBOOT"
echo "|${COMMAND/
}|"
# Outputs |REBOOT|

6

สิ่งที่ได้ผลสำหรับฉันคือ echo $testVar | tr "\n" " "

โดยที่ testVar มีตัวแปร / สคริปต์เอาต์พุตของฉัน


4

การเพิ่มคำตอบเพื่อแสดงตัวอย่างของการขีดทับอักขระหลายตัวรวมถึง \ r โดยใช้ tr และการใช้ sed และการแสดงโดยใช้ฐานสิบหก

ในกรณีของฉันฉันพบว่าคำสั่งที่ลงท้ายด้วยการพิมพ์ awk ของรายการสุดท้าย|awk '{print $2}'ในบรรทัดนั้นมี carriage-return \ r เช่นเดียวกับเครื่องหมายคำพูด

ฉันเคยsed 's/["\n\r]//g'ถอดทั้งแคร่ไป - กลับและเครื่องหมายคำพูด

tr -d '"\r\n'ฉันยังจะได้ใช้

สิ่งที่น่าสนใจsed -zเป็นสิ่งจำเป็นหากมีใครต้องการลบ \ n ตัวอักษรฟีดบรรทัด

$ COMMAND=$'\n"REBOOT"\r   \n'

$ echo "$COMMAND" |hexdump -C
00000000  0a 22 52 45 42 4f 4f 54  22 0d 20 20 20 0a 0a     |."REBOOT".   ..|

$ echo "$COMMAND" |tr -d '"\r\n' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

$ echo "$COMMAND" |sed 's/["\n\r]//g' |hexdump -C
00000000  0a 52 45 42 4f 4f 54 20  20 20 0a 0a              |.REBOOT   ..|

$ echo "$COMMAND" |sed -z 's/["\n\r]//g' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

และนี่คือสิ่งที่เกี่ยวข้อง: carriage return, linefeed และ form feed คืออะไร?

  • CR == \ r == 0x0d
  • LF == \ n == 0x0a


2

หากคุณใช้ bash โดยเปิดใช้งานตัวเลือก extglob คุณสามารถลบเฉพาะช่องว่างต่อท้ายผ่าน:

shopt -s extglob
COMMAND=$'\nRE BOOT\r   \n'
echo "|${COMMAND%%*([$'\t\r\n '])}|"

ผลลัพธ์นี้:

|
RE BOOT|

หรือแทนที่ %% ด้วย ## เพื่อแทนที่เฉพาะช่องว่างนำหน้า


1
สิ่งนี้ทำงานได้อย่างมีเสน่ห์ด้วยผลลัพธ์แปลก ๆ ที่ฉันได้รับจากคำสั่งนักเทียบท่า ขอบคุณมาก!
develCuy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.