ฉันจะลบบรรทัดใหม่ต่อท้ายในทุบตีได้อย่างไร


10

ฉันกำลังมองหาสิ่งที่ชอบทำตัวของ chompPerl ฉันกำลังมองหาคำสั่งที่พิมพ์เพียงแค่อินพุตลบอักขระตัวสุดท้ายหากเป็นบรรทัดใหม่:

$ printf "one\ntwo\n" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done
$ printf "one\ntwo" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done

(การทดแทนคำสั่งใน Bash และ Zsh ลบบรรทัดใหม่ที่ตามมาทั้งหมด แต่ฉันกำลังมองหาบางสิ่งที่จะลบบรรทัดใหม่ที่ต่อท้ายหนึ่งบรรทัด)

คำตอบ:


9

สิ่งนี้น่าจะใช้ได้:

printf "one\ntwo\n" | awk 'NR>1{print PREV} {PREV=$0} END{printf("%s",$0)}' ; echo " done"

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

มันทำอะไรในรายละเอียดเพิ่มเติม:

  1. NR>1{print PREV} พิมพ์บรรทัดก่อนหน้า (ยกเว้นครั้งแรก)
  2. {PREV=$0}เก็บบรรทัดปัจจุบันในPREVตัวแปร
  3. END{printf("%s",$0)} ในที่สุดพิมพ์บรรทัดสุดท้ายโดยไม่ต้องขึ้นบรรทัดใหม่

โปรดทราบว่าสิ่งนี้จะลบบรรทัดว่างเปล่าได้ไม่เกินหนึ่งบรรทัดในตอนท้าย (ไม่รองรับการลบ"one\ntwo\n\n\n")


15

คุณสามารถใช้perlโดยไม่ต้องchomp:

$ printf "one\ntwo\n" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

$ printf "one\ntwo" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

แต่ทำไมไม่ใช้chompตัวเอง:

$ printf "one\ntwo\n" | perl -pe 'chomp if eof'; echo " done"

4

หากคุณต้องการเทียบเท่าตรงกับchompวิธีแรกที่มาถึงใจของฉันเป็นวิธีการแก้ปัญหาที่ awk LatinSuD เอาไว้แล้ว ฉันจะเพิ่มวิธีอื่น ๆ ที่ไม่ได้ใช้chompแต่ใช้งานทั่วไปบางอย่างที่chompมักใช้

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

echo "$(printf 'one\ntwo') done"
echo "$(printf 'one\ntwo\n') done"
echo "$(printf 'one\ntwo\n\n') done"
echo "$(printf 'one\ntwo\n\n\n\n\n\n\n\n\n\n') done"

หากคุณต้องการต่อท้ายข้อความบางข้อความในบรรทัดสุดท้ายของไฟล์หรือเอาต์พุตของคำสั่งคุณsedสามารถทำได้อย่างสะดวก ด้วย GNU sed และการใช้งานที่ทันสมัยอื่น ๆ ส่วนใหญ่ทำงานได้แม้ว่าอินพุตจะไม่สิ้นสุดในการขึ้นบรรทัดใหม่¹; อย่างไรก็ตามสิ่งนี้จะไม่เพิ่มบรรทัดใหม่หากยังไม่มีบรรทัดใหม่

sed '$ s/$/ done/'

¹ อย่างไรก็ตามสิ่งนี้ไม่สามารถใช้ได้กับการใช้งาน sed ทั้งหมด: sed เป็นเครื่องมือประมวลผลข้อความและไฟล์ที่ไม่ว่างเปล่าและไม่ได้ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่ไม่ใช่ไฟล์ข้อความ


นี้ไม่ว่าเทียบเท่ากับchompเป็นchompลบเพียงมากที่สุดคนหนึ่งลากขึ้นบรรทัดใหม่
Flimm

@Flimm ใช่สิ่งที่ชัดเจนที่สุดเทียบเท่ากับchompawk solution ที่ LatinSuD โพสต์ไปแล้ว แต่ในหลายกรณีchompเป็นเพียงเครื่องมือในการทำงานและฉันก็มีวิธีในการทำงานทั่วไปบางอย่าง ให้ฉันอัปเดตคำตอบของฉันเพื่อชี้แจงเรื่องนี้
Gilles 'SO- หยุดความชั่วร้าย'

1

อีกperlแนวทางหนึ่ง สิ่งนี้อ่านอินพุตทั้งหมดลงในหน่วยความจำดังนั้นจึงอาจไม่ควรใช้ข้อมูลจำนวนมาก (ใช้ cuonglm หรือawkวิธีการดังกล่าว)

$ printf "one\ntwo\n" | perl -0777pe 's/\n$//'; echo " done"
one
two done

ขอบคุณ @ StéphaneChazelasแก้ไขแล้ว ด้วยเหตุผลบางอย่างสวิตช์นี้สร้างความสับสนให้ฉันเสมอ !
terdon

0

ฉันขัดขวางสิ่งนี้จาก repit Github ที่ไหนซักแห่ง แต่หาที่ไหนไม่ได้

ลบ-ท้ายว่างสาย-sed

#!/bin/bash
#
# Delete all trailing blank lines.
# From http://sed.sourceforge.net/sed1line.txt
#
# Version: 1.3.0
# Created: 2011-01-02
# Updated: 2015-01-25
# Contact: Joel Parker Henderson (joel@joelparkerhenderson.com)
# License: GPL
##
set -euf
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'

0

นามธรรม

พิมพ์บรรทัดที่ไม่มีบรรทัดใหม่เพิ่มบรรทัดใหม่เฉพาะเมื่อมีอีกบรรทัดที่จะพิมพ์

$ printf 'one\ntwo\n' | 

     awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }';   echo " done"

one
two done

โซลูชั่นอื่น ๆ

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

removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || ตัด -s-1 "$ 1"; }

นั่นเป็นวิธีแก้ปัญหาที่รวดเร็วเนื่องจากต้องการอ่านตัวละครเพียงตัวเดียวจากไฟล์และลบออกโดยตรง ( truncate) โดยไม่ต้องอ่านไฟล์ทั้งหมด

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

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

ตัวแปร

วิธีการแก้ปัญหาความไร้เดียงสาคือการรวบรวมอินพุตทั้งหมดลงในตัวแปร:

FilterOne(){ filecontents=$(cat; echo "x");        # capture the whole input
             filecontents=${filecontents%x};       # Remove the "x" added above.
             nl=$'\n';                             # use a variable for newline.
             printf '%s' "${filecontents%"$nl"}";  # Remove newline (if it exists).
       }

printf 'one\ntwo'     | FilterOne ; echo 1done
printf 'one\ntwo\n'   | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done

หน่วยความจำ

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

ยกเว้น GNU awk ด้วย-zตัวเลือก:

sed -z 's/\(.*\)\n$/\1/'

ด้วย awk (awk ใด ๆ ), กวาดทั้งสตรีมและprintfโดยไม่ต้องขึ้นบรรทัดใหม่

awk '    { content = content $0 RS } 
     END { gsub( "\n$", "", content ); printf( "%s", content ) }
    '

การโหลดไฟล์ทั้งหมดลงในหน่วยความจำอาจไม่ใช่ความคิดที่ดี แต่อาจใช้หน่วยความจำมาก

หน่วยความจำสองบรรทัด

ใน awk เราสามารถประมวลผลสองบรรทัดต่อลูปโดยจัดเก็บบรรทัดก่อนหน้าในตัวแปรและพิมพ์บรรทัดปัจจุบัน:

awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'

การประมวลผลโดยตรง

แต่เราสามารถทำได้ดีกว่า

หากเราพิมพ์บรรทัดปัจจุบันโดยไม่ขึ้นบรรทัดใหม่และพิมพ์บรรทัดใหม่เฉพาะเมื่อมีบรรทัดถัดไปเราจะประมวลผลทีละบรรทัดและบรรทัดสุดท้ายจะไม่มีการขึ้นบรรทัดใหม่:

awk 'NR == 1 {printf ("% s", $ 0); next}; {printf ("\ n% s", $ 0)} '

หรือเขียนด้วยวิธีอื่น:

awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'

หรือ:

awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'

ดังนั้น:

$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.