Bash

用多字元分隔符合併行的優雅方式,忽略空行,支持 n、r 或 rn

  • October 17, 2020

我想bash使用變數中的文件路徑讀取腳本中的多行文件,然後使用多字元分隔符合併行並將結果保存到另一個變數。

我想跳過空行和尾隨新行,並且不想要尾隨分隔符。

此外,我想支持\r\n並且 - 如果沒有進一步的“成本” - 為什麼不也\r作為換行符(當然還有\n)。

該腳本應該使用 GNU 的 bash 4.2.46、sed 4.2.2、awk 4.0.2、grep 2.20、coreutils 8.22(tr、cat、paste、sort、cut、head、tail、tee、…)在 RHEL 上執行, xargs 4.5.11 和 libc 2.17 以及 perl 5.16.3、python 2.7.5 和 openjdk 11.0.8。

它應該每天對帶有 ca 的文件執行大約兩次。一台像樣的機器/虛擬機上有 10 行。如果可讀性、可維護性和簡潔性不會受到太大影響,我對性能更高的解決方案持開放態度。

要讀取的文件可以在同一台機器或其他系統上創建和Win7修改Win10

到目前為止我的方法是

joined_string_var=$(sed 's/\r/\n/g' $filepathvar | grep . | sed ':a; N; $!ba; s/\n/; /g')
  • 因此,首先我替換\r\n涵蓋所有換行符格式並使輸出對 grep 可讀。
  • 然後我刪除空行grep .
  • 最後我使用 sed 進行實際的行合併。

我在第一步中使用sed而不是tr避免使用 cat,但我不太確定我是否更喜歡這樣:

joined_string_var=$(cat $filepathvar | tr '\r' '\n' | grep . | sed ':a; N; $!ba; s/\n/; /g')

**更新:**我不知何故完全錯過了簡單的重定向:

joined_string_var=$(tr '\r' '\n' <$filepathvar | grep . | sed ':a; N; $!ba; s/\n/; /g')

有什麼想法可以更優雅地完成(更少的命令,更好的性能,簡潔性和可讀性更差)?

優雅可能來自正確的正則表達式。您可以將每個行終止符, ,轉換為您想要的分隔符,而不是將 every 更改\r\n( ) (在 GNU sed 中,很少 sed 實現會理解,並不是所有人都會理解):s/\r/\n/g``\r\n``\r``\n``\r``-E

sed -E 's/\r\n|\r|\n/; /g'

或者,如果您想刪除空行,任何此類行終止符的執行:

sed -E 's/[\r\n]+/; /g'

如果我們能夠擷取模式空間中的所有行終止符,這將起作用。這意味著將整個文件放入記憶體中以便能夠編輯它們。

因此,您可以使用更簡單的(GNU sed 的一個命令):

sed -zE 's/[\r\n]+/; /g; s/; $/\n/' "$filepathvar"

-z空字節作為行終止符有效地獲取所有\r\n模式空間。

s/[\r\n]+/; /g所有類型的行分隔符轉換為您想要的字元串。

s/; $/\n/(最後一個)尾隨分隔符轉換為實際的換行符。


筆記

sed 選項意味著使用-z零分隔符 (0x00)。該定界符的使用始於 find 的需要,以便能夠處理帶有與-print0xargs ( -0) 選項匹配的換行符 ( ) 的文件名。這意味著還修改了一些工具以處理零分隔字元串。

這是一個非 posix 選項,它在零而不是換行符處中斷文件。

Posix 文本文件必須沒有零 (NIL) 字節,因此使用該選項實際上意味著在處理之前將整個文件擷取到記憶體中。

在 NIL 上打斷文件意味著換行符在 sed 的模式空間上是可編輯的。如果文件碰巧有一些 NIL 字節,這個想法仍然適用於換行符,因為它們在文件的每個塊中仍然是可編輯的。

-z選項已添加到 GNU sed。ATT sed(posix 所基於)沒有這樣的選項(現在仍然沒有),一些 BSD sed 也仍然沒有。

該選項的另一種-z選擇是在記憶體中擷取整個文件。這可以通過某些方式在 Poixly 中完成:

sed 'H;1h;$!d'          # capture whole file in hold space.
sed ':a;N;$!ba'         # capture whole file in pattern space.

在模式空間中包含所有換行符(最後一個換行符除外)可以編輯它們:

sed -Ee 'H;1h;$!d;x'   -e 's/(\r\n|\r|\n)/; /g

對於較舊的 sed,還需要使用更長且更明確(\r\n|\r|\n)+的,而不是[\r\n]+因為此類 sed 不理解\r\n在括號表達式[]中。

面向線

一次工作一行的解決方案(a\r也是此解決方案中的有效行終止符),這意味著使用 GNU awk 無需將整個文件保存在記憶體中(使用的記憶體更少):

awk -vRS='[\r\n]+' 'NR>1{printf "; "}{printf $0}END{print ""}'  file

由於正則表達式記錄分隔符,必須是 GNU awk [\r\n]+。在其他 awk 中,記錄分隔符必須是單個字節。

引用自:https://unix.stackexchange.com/questions/614451