我有一個包含多行的文件,如果它們都符合特定模式,我想將它們連接起來。
我知道我可以找到適合該模式的行並使用以下命令獲取下一行:
grep -E -A1 'Pattern' filename
但是我如何檢查下一行是否也符合該模式以及如何將兩者連接起來?
例如,我有一個這樣的文件:
Hello
i
am
John
Smith
範例模式可能如下:
'^[A-Z][a-z]+'
因此,在這種情況下,如果行都以大寫字母開頭,我想合併這些行。
我想要實現的輸出是:
Hello
i
am
John Smith
答案1
/^[A-Z][a-z]+/{
:a
N
/\n[A-Z][a-z]+/{
s/\n/ /
b a
}
}
將其另存為join.sed
並執行:sed -Ef join.sed file
。
如果該行與模式匹配,我們將啟動一個循環,將下一行附加到模式空間,並用空格替換換行符,只要該行也與模式匹配。
對於 GNU Sed,你可以將其折疊成一行:
sed -E '/^[A-Z][a-z]+/{:a;N;/\n[A-Z][a-z]+/{s/\n/ /;b a}}' file
或者,一個 Awk 腳本join.awk
,其模式應為p
:
{
if($0~p)c+=1
else c=0
printf "%s%s", (c>1 ? " " : ors), $0
ors=ORS
}
END{print ""}
執行:awk -f join.awk p='^[A-Z][a-z]+' file
。
答案2
使用sed
空字元作為分隔符號 ( -z
):
$ sed -z 's/\([A-Z][a-z]\+\)\n\([A-Z][a-z]\+\)/\1 \2/'
Hello
i
am
John Smith
答案3
使用 Raku(以前稱為 Perl_6)
raku -e 'given lines.join("\n") { S/ $<first>=[<upper><lower>+] \n $<last>=[<upper><lower>+] /$<first> $<last>/.put};'
輸入範例:
Hello
i
am
John
Smith
goodbye
範例輸出:
Hello
i
am
John Smith
goodbye
上面是用 Raku(Perl 語言家族的成員)寫的解。資料given
以 的形式傳送給 Raku lines
,但由於 Raku 的lines
例行自動剪切輸入,資料會join
以換行符號進行 -ed。雖然這看起來有點複雜,但優點是 Raku 的lines
例程可以延遲讀取數據,這意味著上面的程式碼應該記憶體效率高。
Raku 實作了一種S///
「非破壞性」運算符,它與熟悉的s///
運算符類似(如果不相同)(Raku 也有該運算符)。資本運營S
商有一個優勢,那就是“保留原始字串完整併返回結果字串而不是 $/ (匹配變數)。”
在運算符的匹配(左)一半內S///
,命名捕獲被雇用。正規表示式引擎首先搜尋[<upper><lower>+]
並將其指派給命名的 capture $<first>
,然後搜尋一個\n
(換行符),最後搜尋另一個[<upper><lower>+]
,這次將其指派給命名的 capture $<last>
。最後,在運算符的替換(右)半部分中S///
,兩個命名捕獲$<first> $<last>
用於替換左側匹配,儘管和一個空間和沒有\n
之間的換行符。
完成同樣事情的另一種方法如下。該程式碼省略了命名捕獲,而是使用<(\n)>
從匹配對像中刪除除捕獲標記內的內容之外的所有內容<(…)>
。然後在替換中,\n
用空格替換:
raku -e 'put S/ [<upper><lower>+] <(\n)> [<upper><lower>+] / / given lines.join("\n");'
[注意,上面的程式碼只會將George\nHerbert\nWalker\nBush
4 行折疊成 3 行 ( George Herbert\nWalker\nBush
)。如果您希望在一行上傳回所有按行連續出現的情況[<upper><lower>+]
,請隨時發布該問題]。
https://docs.raku.org/language/regexes#S///_non-breaking_substitution
https://docs.raku.org/language/regexes#index-entry-regex__Named_captures-Named_captures
https://raku.org