命令在每行前面添加字符串?


36

寻找这样的东西?有任何想法吗?

cmd | prepend "[ERRORS] "

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

有什么方法可以对bash函数/脚本中的所有命令进行设置吗?
亚历山大·米尔斯

Answers:


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

具有仅使用bash内置函数的优点,因此将更少地创建/销毁进程,因此它的触摸速度比awk或sed更快。

@tzrik指出,它也可能会成为一个不错的bash函数。定义如下:

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

将允许它像这样使用:

cmd | prepend "[ERROR] "

4
实际上,这只会使进程数减少一。(但是可能会更快,因为不使用任何正则sed表达式(awk)甚至不使用字符串拆分()。)
grawity

顺便说一句,我很好奇性能,这是我使用bash,sed和awk进行简单基准测试的结果。将大约1000行文本(dmesg输出)推入FIFO文件,然后按以下方式读取它们:pastebin.ca/1606844看起来awk是赢家。有什么想法吗?
Ilya Zakreuski,2009年

1
请谨慎运行这样的时序测试-尝试以所有6种不同的顺序运行它们,然后取平均结果。不同的顺序可减轻块高速缓存的影响,平均的顺序可减轻背景中断/调度的影响。
2009年

这个问题被标记为“ shell”,而不是“ bash”。
fiatjaf

1
也很容易将其包装在函数中:function prepend() { while read line; do echo "${1}${line}"; done; }
tzrlk

46

尝试这个:

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

干杯


1
这样做的缺点是“ [ERROR]”不能是变量,因为整个表达式必须用单引号引起来。
user1071136

4
awk -vT="[ERROR] " '{ print T $0 }'awk -vT="[ERROR]" '{ print T " " $0 }'
Tino 2014年

2
T="[ERROR] " awk '{ print ENVIRON["T"] $0 }'T="[ERROR]" awk '{ print ENVIRON["T"] " " $0 }'
Tino 2014年

您只需退出引号范围即可取消引用变量:cmd | awk '{print "['$V]' " $0}'-应该在开始时对其进行一次评估,因此不会增加性能开销。
罗伯特

13

感谢@grawity,我正在提交他的评论作为答案,因为这似乎是我的最佳答案。

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

为什么这比bash解决方案更好?
user14645'1

1
我想这取决于您的目的。如果您的目标只是在文件的每一行前添加前缀,则可以使用非常熟悉的工具以很少的字符来实现该目标。我更喜欢10行bash脚本。在awk一个班轮是足够好的,但我认为,越来越多的人所熟悉的sedawk。bash脚本可以很好地完成它的工作,但似乎正在回答一个未被询问的问题。
埃里克·威尔逊

pjz提交的答案也很不错。它没有其他程序,过程,并且运行速度可能更快一些。
user14645'2

3
sed X cmd会读取cmd并且不会执行它。无论是cmd | sed 's/^/[ERROR] /'sed 's/^/[ERROR] /' <(cmd)cmd > >(sed 's/^/[ERROR] /')。但是要当心后者。即使这允许您访问的返回值cmdsed在后台运行,所以它可能是你可以看到输出完成CMD。不过,它非常适合登录文件。请注意,这awk可能比sed
蒂诺2014年

真好 该命令很容易被别名。 alias lpad="sed 's/^/ /'"。我插入4个前导空格而不是错误。现在,对于魔术来说: ls | lpad | pbcopy将ls输出前加4个空格,将其标记为Markdown进行代码,这意味着您将剪贴板(在Mac上为pbcopy抓取剪贴板)直接粘贴到StackOverflow或任何其他markdown上下文中。不能aliasAWK答案(1日试)所以这1个胜场。在 同时读解也是别名能干,但我觉得这sed的更具表现力。
JL Peyret

8

我创建了一个GitHub存储库以进行一些速度测试。

结果是:

  • 在一般情况下,awk最快。 比sedperl一点也不慢多少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,'

和Python无缓冲:

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 ...,将“ [ERROR]”添加到的输出中cmd,并且仍然将stderr和stdout分开。


我尝试了这种方法,但是那些>(子shell发生了一些我无法完全解决的问题。似乎脚本已完成,并且提示返回后,输出已到达终端这有点混乱。我最终在这里想出了答案stackoverflow.com/a/25948606/409638
robert
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.