如何将文本文件中的行拆分为新文件中的两行连续的行?

如何将文本文件中的行拆分为新文件中的两行连续的行?

我有一find -exec grep对命令,它们组合在一起路径/文件名.扩展名:ln#:行内容在一行上。我想在第二个文件中将该行分成两行连续的行。连续的行是:

path/filename/ext:ln#
contents of the line itself

我可以编写一个程序来完成这项工作,但我想知道是否有一个命令可以做到这一点?

答案1

您的问题以及我的理解

您的问题目前缺乏输入和期望输出的具体示例,因此我将尝试回答您的答案据我所知,并在您提供更多信息时进行相应的编辑。

我现在理解你的问题是,你正在进行以下一些事情:

find /path/to/directory -exec grep -H -n 'SomeString' {} \;

产生的结果如下:

$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \;              
/home/serg/fortesting/file3:1:HelloWorld
/home/serg/fortesting/file1:4:HelloWorld

或者一般来说/path/to/file:lineNumber:String

可能的解决方案

非常恰当地说,这是一项工作awk:您有 3 个由冒号(字段分隔符)分隔的字段,转换为 awk 代码awk -F":" '{printf $1 FS $2 FS "\n" $3 "\n" }'因此我们可以执行以下操作:

$ find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }'       
/home/xieerqi/fortesting/file3:1:
HelloWorld
/home/xieerqi/fortesting/file1:4:
HelloWorld

现在,awk是一个多功能工具;我们可以用“find -exec awk '(awk code here)'”来模仿输出find -exec grep,它将已经将被处理,并节省管道。

考虑下面的代码:

$ find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \;                                                  
/home/xieerqi/fortesting/file3:1
HelloWorld
/home/xieerqi/fortesting/file1:4
HelloWorld

更少的管道和内容正在被处理,因为他们被发现。此外,如果文件的名称中有冒号,此代码仍然会正确处理它,因为我们不依赖字段分隔符,而是打印变量FILENAME,后跟冒号,后跟FNR(当前输入文件中的输入记录号),以及用换行符分隔的找到的行。

效率

现在,让我们考虑文件数量增加时的效率。首先,我创建文件file1file1000,然后我们用/usr/bin/time它来测试每个版本的命令。

$ echo 'HelloWorld' | tee file{$(seq -s',' 1 1000)}
$ /usr/bin/time find /home/$USER/fortesting -type f -exec grep -H -n 'HelloWorld' {} \; | awk -F ":" '{printf $1 FS $2 FS "\n" $3 "\n" }'  > /dev/null
0.04user 0.34system 0:03.09elapsed 12%CPU (0avgtext+0avgdata 2420maxresident)k
0inputs+0outputs (0major+113358minor)pagefaults 0swaps

$ /usr/bin/time find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} \; > /dev/null                        
0.82user 2.03system 0:04.25elapsed 67%CPU (0avgtext+0avgdata 2856maxresident)k
0inputs+0outputs (0major+145292minor)pagefaults 0swaps

因此,较长的版本似乎更高效,占用的时间和 CPU 百分比更少。

现在,有一个折衷方案——\;改为+

/usr/bin/time find $PWD -type f -exec awk '/HelloWorld/ {print FILENAME":"NR"\n"$0 }' {} +

操作员做什么+最大的区别是,它+告诉 exec 列出尽可能多的文件作为输入到awk命令中,而\;makesawk每次对找到的每一个文件进行调用。

$ /usr/bin/time find $PWD -type f -exec awk  '/HelloWorld/ {print FILENAME":"FNR"\n"$0 }' {} + > /dev/null                         
0.00user 0.02system 0:00.02elapsed 74%CPU (0avgtext+0avgdata 3036maxresident)k
0inputs+0outputs (0major+398minor)pagefaults 0swaps

嘿,快多了,对吧?虽然 CPU 占用仍然很大。

输出到另一个文件

至于输出到另一个文件,添加使用>运算符进行重定向

答案2

sed 很容易做到这一点:

$ echo 'path/filename.ext:ln#:line contents' | sed -r 's/([^:]*:[^:]*):/\1\n/'
path/filename.ext:ln#
line contents

正则表达式([^:]*:[^:]*):查找前两个以冒号分隔的字段并将它们保存在第 1 组中。替换文本\1\n在两个字段后放置一个换行符。

改进

如果文件名本身包含冒号,这当然会产生错误的结果。正如steeldriver建议的那样,可以使用选项来避免这种情况,该-Z选项grep将在文件名后放置一个NUL字符,\x00而不是冒号。例如:

grep -ZHn 'regex' * | sed -r 's/\x00([^:]*):/:\1\n/'

find或者,如果需要以下功能:

find . -type f -exec grep -ZHn 'regex' {} + | sed -r 's/\x00([^:]*):/:\1\n/'

即使文件名或匹配的行中出现冒号,或者两者兼而有之,这也能起作用。

相关内容