
我有這樣的文件:
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