Lệnh để nối lại chuỗi cho mỗi dòng?


36

Tìm kiếm một cái gì đó như thế này? Có ý kiến ​​gì không?

cmd | prepend "[ERRORS] "

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

Có cách nào để thiết lập điều này cho tất cả các lệnh trong hàm bash / script không?
Alexander Mills

Câu trả lời:


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

có lợi thế là chỉ sử dụng các nội dung bash để tạo ra / phá hủy ít quy trình hơn nên nó sẽ nhanh hơn một chút so với awk hoặc sed.

@tzrik chỉ ra rằng nó cũng có thể tạo ra một hàm bash đẹp. Xác định nó như sau:

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

sẽ cho phép nó được sử dụng như:

cmd | prepend "[ERROR] "

4
Điều này thực sự chỉ làm giảm quá trình đếm một. (Nhưng nó có thể nhanh hơn vì không sử dụng regexps ( sed) hoặc thậm chí tách chuỗi ( awk).)
grawity

BTW, tôi đã tò mò về hiệu suất và đây là kết quả của điểm chuẩn đơn giản của tôi bằng cách sử dụng bash, sed và awk. Đẩy khoảng 1000 dòng văn bản (đầu ra dmesg) vào tệp FIFO và sau đó đọc chúng như thế này: pastebin.ca/1606844 Có vẻ như awk là người chiến thắng. Bất cứ ý tưởng tại sao?
Ilya Zakreuski

1
hãy cẩn thận khi chạy các bài kiểm tra thời gian như thế - hãy thử chạy chúng trong tất cả 6 đơn hàng khác nhau và sau đó lấy trung bình các kết quả. Các lệnh khác nhau để giảm thiểu các hiệu ứng bộ đệm và trung bình để giảm thiểu các hiệu ứng gián đoạn / lập lịch nền.
pjz

Câu hỏi này được gắn thẻ "shell", không phải "bash".
fiatjaf

1
Cũng đủ dễ dàng để bọc nó trong một chức năng:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk

46

Thử đi:

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

Chúc mừng


1
Điều này có nhược điểm là "[ERROR]" không thể là một biến, bởi vì toàn bộ biểu thức phải ở trong dấu ngoặc đơn.
dùng1071136

4
awk -vT="[ERROR] " '{ print T $0 }'hoặcawk -vT="[ERROR]" '{ print T " " $0 }'
Tino

2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'hoặcT="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Tino

Bạn chỉ có thể thoát khỏi phạm vi của các trích dẫn để loại bỏ biến: cmd | awk '{print "['$V]' " $0}'- điều này nên được đánh giá một lần khi bắt đầu, do đó không có chi phí hiệu năng.
robert

13

Với tất cả tín dụng dành cho @grawity, tôi đang gửi bình luận của anh ấy như một câu trả lời, vì đây có vẻ là câu trả lời tốt nhất ở đây cho tôi.

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

Tại sao điều này thích hợp hơn với giải pháp bash?
dùng14645

1
Tôi cho rằng nó phụ thuộc vào mục đích của bạn. Nếu mục tiêu của bạn chỉ đơn giản là trả trước cho mỗi dòng trong một tệp, thì mục tiêu này sẽ hoàn thành mục tiêu đó với rất ít ký tự, sử dụng một công cụ rất quen thuộc. Tôi thích điều đó hơn với một kịch bản bash 10 dòng. Một awklớp lót là đủ tốt, nhưng tôi nghĩ rằng nhiều người quen thuộc sedhơn awk. Kịch bản bash tốt cho những gì nó làm, nhưng có vẻ như nó đang trả lời một câu hỏi không được hỏi.
Eric Wilson

Câu trả lời mà pjz gửi cũng là một lót tốt đẹp. Nó không bổ sung các chương trình, quy trình và có thể chạy nhanh hơn một chút.
user14645

3
sed X cmdkhông đọc cmdvà không thực hiện nó. Hoặc cmd | sed 's/^/[ERROR] /'hoặc sed 's/^/[ERROR] /' <(cmd)hoặc cmd > >(sed 's/^/[ERROR] /'). Nhưng hãy cẩn thận sau. Thậm chí điều này cho phép bạn truy cập giá trị trả về của cmdcác sedlần chạy trong nền, vì vậy có khả năng bạn sẽ thấy đầu ra sau khi cmd kết thúc. Tốt để đăng nhập vào một tập tin, mặc dù. Và lưu ý rằng awkcó lẽ là nhanh hơn sed.
Tino

Tốt đẹp. Lệnh này dễ dàng được đặt bí danh. alias lpad="sed 's/^/ /'". thay vì LRI tôi chèn 4 khoảng trắng hàng đầu. Bây giờ, đối với trò ảo thuật: ls | lpad | pbcopysẽ bổ sung đầu ra ls với 4 khoảng trắng đánh dấu nó là Markdown cho , nghĩa là bạn dán bảng tạm ( pbcopy lấy nó, trên mac) trực tiếp vào StackOverflow hoặc bất kỳ bối cảnh đánh dấu nào khác. Không thể aliascác awk câu trả lời (ngày 1 thử) vì vậy đây là một chiến thắng. Các giải pháp trong khi đọc cũng có khả năng bí danh, nhưng tôi thấy sed này biểu cảm hơn.
JL Peyret

8

Tôi đã tạo một kho lưu trữ GitHub để thực hiện một số bài kiểm tra tốc độ.

Kết quả là:

  • Trong trường hợp chung, awklà nhanh nhất. sedchậm hơn một chút và perlkhông chậm hơn nhiều sed. Rõ ràng, tất cả những thứ đó là ngôn ngữ được tối ưu hóa cao để xử lý văn bản.
  • Trong các tình huống rất đặc biệt, nơi các nhánh chiếm ưu thế, chạy tập lệnh của bạn dưới dạng tập kshlệnh được biên dịch ( shcomp) có thể tiết kiệm nhiều thời gian xử lý hơn. Ngược lại, bashlà chết chậm so với kshcác kịch bản được biên dịch .
  • Tạo một nhị phân liên kết tĩnh để đánh bại awkdường như không đáng nỗ lực.

Ngược lại pythonlà chết chậm, nhưng tôi đã không kiểm tra một trường hợp được biên dịch, bởi vì đó thường không phải là những gì bạn sẽ làm trong trường hợp kịch bản như vậy.

Các biến thể sau đây được thử nghiệm:

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] $_"'

Hai biến thể nhị phân của một trong những công cụ của tôi (tuy nhiên nó không được tối ưu hóa cho tốc độ):

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

Bộ đệm Python:

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

Và Python không có bộ đệm:

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 }'để xuất dấu thời gian
Tino


3

Tôi muốn một giải pháp xử lý thiết bị xuất chuẩn và thiết bị xuất chuẩn, vì vậy tôi đã viết prepend.shvà đưa nó vào đường dẫn của mình:

#!/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)

Bây giờ tôi chỉ có thể chạy prepend.sh "[ERROR]" cmd ..., để thêm "[ERROR]" vào đầu ra của cmd, và vẫn có stderr và stdout riêng biệt.


Tôi đã thử cách tiếp cận này nhưng có một cái gì đó đang xảy ra với những >(khung con mà tôi không thể giải quyết được. Có vẻ như tập lệnh đã hoàn tất, và đầu ra đang đến thiết bị đầu cuối sau khi lời nhắc đã trở lại, hơi lộn xộn. Cuối cùng tôi đã đưa ra câu trả lời ở đây stackoverflow.com/a/25948606/409638
robert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.