我有这样的文件:
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
我想生成这样的文件:
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
如何使用 Bash/Sed/Awk/Grep 或类似的东西来做到这一点?
答案1
如果您有一个支持记录分隔符正则表达式的 Awk RS
,则可以这样做:
awk 'BEGIN { RS = " +| *\\\\?\\n" } 1'
这样做的好处是我们不会将整个文件放入内存并进行一些正则表达式替换;您的输入可能有千兆字节长。
我们基本上将文件视为具有两个记录分隔符:一个或多个空格,或者零个或多个空格,后跟换行符,换行符前面可以有一个可选的反斜杠。
以这种方式分隔记录后,我们要做的就是输出它们,后面跟着默认的输出记录分隔符 ( ORS
),当然,它是换行符。这是通过由 组成的模式动作规则实现的1
。
sed
或者使用and进行管道作业tr
,不使用 POSIX 中不存在的任何内容:
tr '\n' ' ' | sed -e 's/\\//g' -e 's/ \+/ /g' | tr ' ' '\n'
用空格替换换行符。然后将多个空格压缩为一个空格,同时删除反斜杠。然后将空格映射到换行符。
答案2
与GNUgrep
$ cat file
Dir1/File1.cpp Dir2/File2.cpp \
Dir3/File1.h Dir4/File2.cpp \
Dir2/File1.cpp \
Dir2/File1.h \
$ grep -o '[^\ ]*' file
Dir1/File1.cpp
Dir2/File2.cpp
Dir3/File1.h
Dir4/File2.cpp
Dir2/File1.cpp
Dir2/File1.h
-o
只提取匹配的模式[^\ ]*
零个或多个非空格和非\
字符,因为*
是贪婪的,所以它将尝试匹配尽可能多的此类字符
要将结果保存到另一个文件,请使用
$ grep -o '[^\ ]*' file > out_file
正如@Stéphane Chazelas 所指出的,最好使用以下内容以提高便携性:
grep -oE '[^\ ]+' file
其中-E
调用扩展正则表达式并[^\ ]+
匹配一个或多个非空格和非\
字符
性能分析:
$ perl -ne 'print "$_"x100000' file > file_big
$ shuf file_big -o file_big
$ du -sh file_big
9.0M file_big
用于比较的评论中的所有答案和建议:
$ time grep -o '[^\ ]*' file_big > o1
real 0m2.090s
user 0m2.076s
sys 0m0.016s
$ time grep -oE '[^\ ]+' file_big > o2
real 0m1.523s
user 0m1.504s
sys 0m0.012s
$ time awk 'BEGIN { RS = " +| *\\\\?\\n" } 1' file_big > o3
real 0m0.331s
user 0m0.320s
sys 0m0.008s
$ time tr -s '\\ ' '[\n*]' < file_big | grep . > o4
real 0m0.095s
user 0m0.124s
sys 0m0.008s
$ time tr '\\ ' '[\n*]' < file_big | grep . > o5
real 0m0.105s
user 0m0.104s
sys 0m0.016s
完整性检查
$ diff -s o1 o2
Files o1 and o2 are identical
$ diff -s o1 o3
Files o1 and o3 are identical
$ diff -s o1 o4
Files o1 and o4 are identical
$ diff -s o1 o5
Files o1 and o5 are identical