คำสั่งเพื่อเพิ่มสตริงลงในแต่ละบรรทัด?


36

กำลังมองหาอะไรแบบนี้? ความคิดใด ๆ

cmd | prepend "[ERRORS] "

[ERROR] line1 text
[ERROR] line2 text
[ERROR] line3 text
... etc

มีวิธีการตั้งค่านี้สำหรับคำสั่งทั้งหมดในฟังก์ชั่นทุบตี / สคริปต์?
Alexander Mills

คำตอบ:


39
cmd | while read line; do echo "[ERROR] $line"; done

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

@tzrik ชี้ให้เห็นว่ามันอาจทำให้ฟังก์ชั่นทุบตีดี กำหนดเช่น:

function prepend() { while read line; do echo "${1}${line}"; done; }

จะอนุญาตให้ใช้เช่น:

cmd | prepend "[ERROR] "

4
การทำเช่นนี้จะลดขั้นตอนการนับเพียงครั้งเดียวเท่านั้น ( แต่มันอาจจะเร็วขึ้นเพราะไม่มี regexps ( sed) หรือแม้กระทั่งการแยกสตริง ( awk) จะใช้.)
grawity

BTW ฉันอยากรู้เกี่ยวกับประสิทธิภาพและนี่คือผลลัพธ์ของมาตรฐานที่เรียบง่ายของฉันโดยใช้ bash, sed และ awk กดข้อความประมาณ 1000 บรรทัด (เอาต์พุต dmesg) ไปที่ไฟล์ FIFO แล้วอ่านพวกเขาเช่นนี้: pastebin.ca/1606844ดูเหมือนว่า awk เป็นผู้ชนะ ความคิดใดทำไม
Ilya Zakreuski

1
ใช้ความระมัดระวังในการทดสอบกำหนดเวลา - ลองใช้คำสั่งที่แตกต่างกันทั้ง 6 คำสั่งจากนั้นจึงเฉลี่ยผลลัพธ์ คำสั่งต่าง ๆ เพื่อบรรเทาผลกระทบบล็อกแคชและค่าเฉลี่ยเพื่อลดผลกระทบจากการหยุดชะงัก / การตั้งเวลา
pjz

คำถามนี้ถูกแท็ก "shell" ไม่ใช่ "bash"
fiatjaf

1
ง่ายพอที่จะหุ้มด้วยฟังก์ชันเช่นกัน:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk

46

ลองสิ่งนี้:

cmd | awk '{print "[ERROR] " $0}'

ไชโย


1
นี่เป็นข้อเสียที่ "[ข้อผิดพลาด]" ไม่สามารถเป็นตัวแปรได้เนื่องจากนิพจน์ทั้งหมดต้องอยู่ในเครื่องหมายคำพูดเดี่ยว
user1071136

4
awk -vT="[ERROR] " '{ print T $0 }'หรือawk -vT="[ERROR]" '{ print T " " $0 }'
Tino

2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'หรือT="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Tino

คุณสามารถออกจากขอบเขตของเครื่องหมายคำพูดเพื่อลดความแปรปรวนของตัวแปร: cmd | awk '{print "['$V]' " $0}'- สิ่งนี้ควรได้รับการประเมินครั้งเดียวเมื่อเริ่มต้นดังนั้นจึงไม่มีค่าใช้จ่ายด้านประสิทธิภาพ
เบิร์ต

13

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

sed 's/^/[ERROR] /' cmd

เหตุใดจึงดีกว่าโซลูชันทุบตี
user14645

1
ฉันคิดว่ามันขึ้นอยู่กับวัตถุประสงค์ของคุณ หากเป้าหมายของคุณคือเติมทุกบรรทัดในไฟล์สิ่งนี้จะบรรลุเป้าหมายนั้นด้วยตัวละครน้อยมากโดยใช้เครื่องมือที่คุ้นเคย ฉันชอบมันมากกว่า 10 bash script awkหนึ่งซับก็เพียงพอที่ดี แต่ผมคิดว่าผู้คนมากขึ้นมีความคุ้นเคยกับกว่าsed awkสคริปต์ทุบตีนั้นดีสำหรับสิ่งที่ทำ แต่ดูเหมือนว่าจะตอบคำถามที่ไม่ได้ถาม
Eric Wilson

คำตอบที่ pjz ส่งนั้นเป็นหนึ่งซับที่ดี มันไม่ได้เพิ่มโปรแกรมกระบวนการและอาจทำงานได้เร็วขึ้นเล็กน้อย
user14645

3
sed X cmdอ่านcmdและไม่ดำเนินการ อย่างใดอย่างหนึ่งcmd | sed 's/^/[ERROR] /'หรือหรือsed 's/^/[ERROR] /' <(cmd) cmd > >(sed 's/^/[ERROR] /')แต่ระวังหลัง แม้ว่าสิ่งนี้จะช่วยให้คุณเข้าถึงค่าส่งคืนของcmdการsedรันในพื้นหลังดังนั้นจึงเป็นไปได้ว่าคุณจะเห็นผลลัพธ์หลังจาก cmd เสร็จสิ้น ดีสำหรับการเข้าสู่ไฟล์แม้ว่า และโปรดทราบว่าอาจจะเร็วกว่าawk sed
Tino

ดี คำสั่งนี้ใช้นามแฝงได้ง่าย alias lpad="sed 's/^/ /'". แทนข้อผิดพลาดฉันใส่ 4 ช่องว่างนำหน้า ตอนนี้สำหรับเคล็ดลับมายากลที่ ls | lpad | pbcopyจะส่งออกย่อหน้า LS 4 พื้นที่ซึ่งเป็นเครื่องหมายMarkdownสำหรับรหัสซึ่งหมายความว่าคุณวางคลิปบอร์ด ( pbcopyคว้ามันบนแม็ค) โดยตรงใน StackOverflow หรือบริบท markdown อื่น ๆ ไม่สามารถawkคำตอบ (ในวันที่ 1 ลอง) ดังนั้นนี้หนึ่งชนะ ในขณะที่อ่านวิธีการแก้ปัญหายังเป็นนามแฝงสามารถ แต่ฉันพบนี้sedแสดงออกมากขึ้น alias
JL Peyret

8

ฉันสร้างที่เก็บ GitHubเพื่อทำการทดสอบความเร็ว

ผลลัพธ์คือ:

  • ในกรณีทั่วไปawkคือเร็วที่สุด sedช้าลงเล็กน้อยและperlไม่ช้ากว่าsedนี้มากนัก เห็นได้ชัดว่าทุกคนเป็นภาษาที่เหมาะที่สุดสำหรับการประมวลผลข้อความ
  • ในสถานการณ์ที่พิเศษมาก ๆ ที่ส้อมครองการรันสคริปต์ของคุณเป็นสคริปต์ที่รวบรวมksh( shcomp) สามารถประหยัดเวลาในการประมวลผลได้มากขึ้น ในทางตรงกันข้ามbashช้ามากเมื่อเทียบกับkshสคริปต์ที่รวบรวม
  • การสร้างไบนารีที่เชื่อมโยงแบบคงที่เพื่อเอาชนะawkดูเหมือนจะไม่คุ้มค่ากับความพยายาม

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

ตัวแปรต่อไปนี้ผ่านการทดสอบแล้ว:

while read line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST] $line"; done
while read -r line; do echo "[TEST]" $line; done
while read -r line; do echo "[TEST]" "$line"; done
sed 's/^/[TEST] /'
awk '{ print "[TEST] " $0 }'
awk -vT="[TEST] " '{ print T $0 }'
awk -vT="[TEST]" '{ print T " " $0 }'
awk -vT="[TEST]" 'BEGIN { T=T " "; } { print T $0 }'
T="[TEST] " awk '{ print ENVIRON["T"] $0 }'
T="[TEST]" awk '{ print ENVIRON["T"] " " $0 }'
T="[TEST]" awk 'BEGIN { T=ENVIRON["T"] " " } { print T $0 }'
perl -ne 'print "[TEST] $_"'

ตัวแปรไบนารีสองตัวในหนึ่งในเครื่องมือของฉัน (ซึ่งไม่ได้ปรับให้เหมาะสมกับความเร็ว):

./unbuffered.dynamic -cp'[TEST] ' -q ''
./unbuffered.static -cp'[TEST] ' -q ''

Python บัฟเฟอร์:

python -uSc 'import sys
for line in sys.stdin: print "[TEST]",line,'

และงูหลาม unbuffered:

python -uSc 'import sys
while 1:
 line = sys.stdin.readline()
 if not line: break
 print "[TEST]",line,'

awk -v T="[TEST %Y%m%d-%H%M%S] " '{ print strftime(T) $0 }'เพื่อส่งออกการประทับเวลา
Tino


3

ฉันต้องการโซลูชันที่จัดการ stdout และ stderr ดังนั้นฉันจึงเขียนprepend.shและนำไปใช้ในเส้นทางของฉัน:

#!/bin/bash

prepend_lines(){
  local prepended=$1
  while read line; do
    echo "$prepended" "$line"
  done
}

tag=$1

shift

"$@" > >(prepend_lines "$tag") 2> >(prepend_lines "$tag" 1>&2)

ตอนนี้ฉันสามารถเรียกใช้prepend.sh "[ERROR]" cmd ...เพื่อเพิ่ม "[ข้อผิดพลาด]" ไปยังเอาต์พุตของcmdและยังคงมี stderr และ stdout แยกกัน


ฉันลองใช้วิธีนี้ แต่มีบางอย่างเกิดขึ้นกับ>(subshell เหล่านั้นที่ฉันไม่สามารถแก้ไขได้ ดูเหมือนว่าสคริปต์จะเสร็จสิ้นแล้วและผลลัพธ์ก็มาถึง terminal หลังจากที่แจ้งกลับมาซึ่งค่อนข้างยุ่ง ในที่สุดฉันก็พบคำตอบได้ที่นี่stackoverflow.com/a/25948606/409638
robert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.