คุณจะรวมบรรทัดทั้งหมดที่ลงท้ายด้วยอักขระเครื่องหมายทับขวาได้อย่างไร


35

การใช้เครื่องมือบรรทัดคำสั่งทั่วไปเช่น sed หรือ awk เป็นไปได้หรือไม่ที่จะรวมบรรทัดทั้งหมดที่ลงท้ายด้วยอักขระที่กำหนดเช่นแบ็กสแลช?

ตัวอย่างเช่นให้ไฟล์:

foo bar \
bash \
baz
dude \
happy

ฉันต้องการเอาท์พุทนี้:

foo bar bash baz
dude happy

1
ส่งไฟล์ผ่านcpp:)
imz - Ivan Zakharyaschev

คำตอบที่ยอดเยี่ยมมากมายฉันหวังว่าฉันสามารถทำเครื่องหมายพวกเขาทั้งหมดเป็นคำตอบ! ขอบคุณสำหรับการดูดีที่ awk, sed และ perl เหล่านี้เป็นตัวอย่างที่ดี
Cory Klein

คำตอบ:


26

โซลูชัน sed ที่สั้นและง่ายกว่า:

sed  '
: again
/\\$/ {
    N
    s/\\\n//
    t again
}
' textfile

หรือหนึ่งซับถ้าใช้ GNU sed:

sed ':x; /\\$/ { N; s/\\\n//; tx }' textfile

1
ดีมาก ... ฉันเริ่มมองที่นี่และไม่เข้าใจมัน (ดังนั้นมันจึงไม่ได้อยู่ในตะกร้าที่แข็งเกินไป) ... แต่หลังจากมองลึกลงไปในคำตอบของ Gilles (ซึ่งใช้เวลาค่อนข้างนาน) ฉันได้ดูคำตอบของคุณอีกครั้งและมันดูเข้าใจได้อย่างน่าทึ่งฉันคิดว่าฉันเริ่มเข้าใจsed:) ... คุณกำลังต่อท้ายแต่ละบรรทัดโดยตรงกับ pattern-space และเมื่อบรรทัด "จบลงปกติ" เข้ามา พื้นที่รูปแบบทั้งหมดลดลงและพิมพ์อัตโนมัติ (เพราะไม่มีตัวเลือก -n) ... เรียบร้อย! .. +1
Peter.O

@fred: ขอบคุณฉันคิดว่าฉันเริ่มที่จะเข้าใจ sed เกินไปก็มีเครื่องมือที่ดีสำหรับการแก้ไขหลาย แต่วิธีการผสมขึ้นพวกเขาจะได้รับสิ่งที่คุณต้องการไม่ตรงไปตรงมาไม่สามารถอ่านได้ที่ด้านบน ...
neurino

ระวังการสิ้นสุดสาย DOS หรือที่รู้จัก ผลตอบแทนการขนส่งหรือ \ r!
user77376

1
เกิดอะไรขึ้นกับsed -e :a -e '/\\$/N; s/\\\n//; ta'
Isaac

18

มันอาจเป็นเรื่องง่ายที่สุดสำหรับ perl (เนื่องจาก perl เหมือน sed และ awk ฉันหวังว่ามันจะเป็นที่ยอมรับของคุณ):

perl -p -e 's/\\\n//'

สั้นและง่ายผมชอบที่หนึ่ง 1 และเขาไม่ได้ขอ sed หรือ awk explicitely
rudolfson

17

นี่คือวิธีแก้ปัญหา awk หากบรรทัดลงท้ายด้วย a \ให้ถอดเครื่องหมายแบ็กสแลชและพิมพ์บรรทัดโดยไม่ต้องขึ้นบรรทัดใหม่ มิฉะนั้นพิมพ์บรรทัดด้วยการขึ้นบรรทัดใหม่

awk '{if (sub(/\\$/,"")) printf "%s", $0; else print $0}'

นอกจากนี้ยังไม่ได้แย่เกินไปในการนั่งดู แต่ awk นั้นอ่านง่ายขึ้น


2

นี่ไม่ใช่คำตอบเช่นนี้ sedมันเป็นปัญหาที่เกี่ยวกับด้านข้าง

โดยเฉพาะฉันต้องใช้sedคำสั่งGilles แยกเป็นชิ้น ๆ เพื่อที่จะเข้าใจมัน ... ฉันเริ่มเขียนบันทึกลงไปแล้วคิดว่ามันอาจจะมีประโยชน์สำหรับคนที่นี่ ...

ดังนั้นนี่คือ ... สคริปต์ sed ของ Gilles ในรูปแบบเอกสาร :


#!/bin/bash
#######################################
sed_dat="$HOME/ztest.dat"
while IFS= read -r line ;do echo "$line" ;done <<'END_DAT' >"$sed_dat"
foo bar \
bash \
baz
dude \
happy
yabba dabba 
doo
END_DAT

#######################################
sedexec="$HOME/ztest.sed"
while IFS= read -r line ;do echo "$line" ;done <<'END-SED' >"$sedexec"; \
sed  -nf "$sedexec" "$sed_dat"

  s/\\$//        # If a line has trailing '\', remove the '\'
                 #    
  t'Hold-append' # branch: Branch conditionally to the label 'Hold-append'
                 #         The condition is that a replacement was made.
                 #         The current pattern-space had a trailing '\' which  
                 #         was replaced, so branch to 'Hold-apend' and append 
                 #         the now-truncated line to the hold-space
                 #
                 # This branching occurs for each (successive) such line. 
                 #
                 # PS. The 't' command may be so named because it means 'on true' 
                 #     (I'm not sure about this, but the shoe fits)  
                 #
                 # Note: Appending to the hold-space introduces a leading '\n'   
                 #       delimiter for each appended line
                 #  
                 #   eg. compare the hex dump of the follow 4 example commands:  
                 #       'x' swaps the hold and patten spaces
                 #
                 #       echo -n "a" |sed -ne         'p' |xxd -p  ## 61 
                 #       echo -n "a" |sed -ne     'H;x;p' |xxd -p  ## 0a61
                 #       echo -n "a" |sed -ne   'H;H;x;p' |xxd -p  ## 0a610a61
                 #       echo -n "a" |sed -ne 'H;H;H;x;p' |xxd -p  ## 0a610a610a61

   # No replacement was made above, so the current pattern-space
   #   (input line) has a "normal" ending.

   x             # Swap the pattern-space (the just-read "normal" line)
                 #   with the hold-space. The hold-space holds the accumulation
                 #   of appended  "stripped-of-backslah" lines

   G             # The pattern-space now holds zero to many "stripped-of-backslah" lines
                 #   each of which has a preceding '\n'
                 # The 'G' command Gets the Hold-space and appends it to 
                 #   the pattern-space. This append action introduces another
                 #   '\n' delimiter to the pattern space. 

   s/\n//g       # Remove all '\n' newlines from the pattern-space

   p             # Print the pattern-space

   s/.*//        # Now we need to remove all data from the pattern-space
                 # This is done as a means to remove data from the hold-space 
                 #  (there is no way to directly remove data from the hold-space)

   x             # Swap the no-data pattern space with the hold-space
                 # This leaves the hold-space re-initialized to empty...
                 # The current pattern-space will be overwritten by the next line-read

   b             # Everything is ready for the next line-read. It is time to make 
                 # an unconditional branch  the to end of process for this line
                 #  ie. skip any remaining logic, read the next line and start the process again.

  :'Hold-append' # The ':' (colon) indicates a label.. 
                 # A label is the target of the 2 branch commands, 'b' and 't'
                 # A label can be a single letter (it is often 'a')
                 # Note;  'b' can be used without a label as seen in the previous command 

    H            # Append the pattern to the hold buffer
                 # The pattern is prefixed with a '\n' before it is appended

END-SED
#######

1
วิธีแก้ปัญหาของ Neurino นั้นเรียบง่ายจริงๆ การพูดของความซับซ้อน sed อย่างอ่อนโยนนี้คุณอาจจะสนใจ
Gilles 'หยุดความชั่วร้าย' ใน

2

เครื่องมือบรรทัดคำสั่งทั่วไปอีกอย่างหนึ่งedซึ่งโดยปกติแล้วจะทำการแก้ไขไฟล์ในสถานที่ดังนั้นจึงไม่มีการแก้ไขสิทธิ์อนุญาตไฟล์ (สำหรับข้อมูลเพิ่มเติมเกี่ยวกับedดูการแก้ไขไฟล์ด้วยโปรแกรมแก้ไขข้อความ ed จากสคริปต์ )

str='
foo bar \
bash 1 \
bash 2 \
bash 3 \
bash 4 \
baz
dude \
happy
xxx
vvv 1 \
vvv 2 \
CCC
'

# We are using (1,$)g/re/command-list and (.,.+1)j to join lines ending with a '\'
# ?? repeats the last regex search.
# replace ',p' with 'wq' to edit files in-place
# (using Bash and FreeBSD ed on Mac OS X)
cat <<-'EOF' | ed -s <(printf '%s' "$str")
H
,g/\\$/s///\
.,.+1j\
??s///\
.,.+1j
,p
EOF

2

การใช้ความจริงที่ว่าreadในเปลือกจะตีความแบ็กสแลชเมื่อใช้โดยไม่-r:

$ while IFS= read line; do printf '%s\n' "$line"; done <file
foo bar bash baz
dude happy

โปรดทราบว่าสิ่งนี้จะตีความแบ็กสแลชอื่น ๆในข้อมูลด้วย


Nope มันจะไม่ลบเครื่องหมายแบ็กสแลชทั้งหมด ลองกับa\\b\\\\\\\\\\\c
ไอแซค

@Isaac Ah ฉันน่าจะพูดว่า "แปลแบ็กสแลชอื่น ๆ " ได้ไหม?
Kusalananda

1

โซลูชัน (r) แบบง่ายที่โหลดไฟล์ทั้งหมดในหน่วยความจำ:

sed -z 's/\\\n//g' file                   # GNU sed 4.2.2+.

หรือสั้น ๆ ซึ่งใช้งานได้กับความเข้าใจ (ผลลัพธ์) บรรทัด (ไวยากรณ์ GNU):

sed ':x;/\\$/{N;bx};s/\\\n//g' file

หนึ่งบรรทัด (ไวยากรณ์ POSIX):

sed -e :x -e '/\\$/{N;bx' -e '}' -e 's/\\\n//g' file

หรือใช้ awk (หากไฟล์ใหญ่เกินไปที่จะใส่ในหน่วยความจำ):

awk '{a=sub(/\\$/,"");printf("%s%s",$0,a?"":RS)}' file

0

รุ่น Mac ที่ใช้โซลูชั่น @Giles จะมีลักษณะเช่นนี้

sed ':x
/\\$/{N; s|\\'$'\\n||; tx
}' textfile

โดยที่ความแตกต่างหลักคือวิธีการขึ้นบรรทัดใหม่และการรวมใด ๆ เพิ่มเติมลงในหนึ่งบรรทัดแบ่ง


-1

คุณสามารถใช้ cpp ได้ แต่มันสร้างบรรทัดว่างบางส่วนที่รวมเอาท์พุทและการแนะนำบางอย่างที่ฉันลบด้วย sed - บางทีมันสามารถทำได้ด้วย cpp-flag และตัวเลือกเช่นกัน:

echo 'foo bar \
bash \
baz
dude \
happy' | cpp | sed 's/# 1 .*//;/^$/d'
foo bar bash baz
dude happy

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

@ manatwork: Outsch! :) ฉันประหลาดใจที่คำสั่ง sed ทำงานได้ แต่แน่นอนมันไม่ใช่คำสั่ง sed แต่ bash นั้นตีความ backslash-linebreak เป็นการต่อเนื่องของบรรทัดก่อนหน้า
ไม่ทราบผู้ใช้

การใช้งานcppแบบที่ยังไม่ต่อสายให้ฉัน และการใช้งานsedนั้นไม่จำเป็นอย่างแน่นอน ใช้cpp -P:“ -Pยับยั้งการสร้าง linemarkers ในเอาต์พุตจากตัวประมวลผลล่วงหน้า” - man cpp
manatwork

คำสั่งของคุณใช้ไม่ได้สำหรับฉัน: cpp: “-P: No such file or directory cpp: warning: '-x c' after last input file has no effect cpp: unrecognized option '-P:' cpp: no input filesการcpp --versionเปิดเผยcpp (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3- อะไรนะ? Ubuntu กำลังแก้ไข cpp หรือไม่ ทำไม? ฉันคาดว่าจะอ่าน GNU ...
ผู้ใช้ไม่ทราบ

น่าสนใจ อูบุนตูcppก็ต่อสายและตัดช่องว่าง แม้จะน่าสนใจมากขึ้นรุ่นเดียวกัน 4.4.3-4ubuntu5.1 -Pนี่ยอมรับ อย่างไรก็ตามมันจะกำจัด linemarkers เท่านั้นบรรทัดว่างยังคงอยู่
ผลิต
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.