Bash

靈活的模式匹配

  • April 11, 2022

我有一個看起來像這樣的文件:

文件1:

0/28
7200/11
14400/11
21584/28
21600/11
28800/28
36000/11
36000/28
43200/11
43200/28
50400/11
57600/11
79200/28

在左側(在 / 之前)我有以秒為單位的時間,而在右側我有一個相應秒的參數值。

現在我有另一個看起來像這樣的文件:

文件2:

0 14
0 15
0 20
0 28
7200 11
7200 14
7200 15

現在,我想從第二個文件中刪除 FILE1 中的公共值。例如,我應該從 FILE2 中刪除:

0 28
7200 11

並保持其餘行不變。

我正在考慮在 bash 腳本中為 FILE1 中的每一行使用 for 循環,然後在 FILE2 中搜尋它,但我無法辨識該模式。如果我嘗試從 awk 使用 substr 它將不起作用,因為時間沒有相同的數字(0 有 1 個數字,7200 有 4 個數字)。

要閱讀 FILE1 我正在做這樣的事情:

IFS=$'\n' read -d '' -r -a X < ./FILE1.csv

為了編寫 for 循環,我正在做這樣的事情:

for x in "${X[@]}"
do
   gawk -i inplace -v var=${x} '{...}' FILE2.csv
done

我也在考慮將 FILE1 轉換為這樣的東西:

0 28
7200 11
14400 11
21584 28
21600 11
28800 28
36000 11
36000 28
43200 11
43200 28
50400 11
57600 11
79200 28

基本上有 2 列,但使用我上面使用的 for 和 var,如果我有超過 1 列,則將不起作用。我認為第二種方法更好,但我不知道如何讓它單獨處理每一列。

編輯:

如果 FILE1 是,我將如何做到這一點:

0 28
7200 11
14400 11
21584 28
21600 11
28800 28
36000 11
36000 28
43200 11
43200 28
50400 11
57600 11
79200 28

文件 2 是:

0 14 2 19
0 15 157 67
0 20 28 57
0 28 25 67
7200 11 88 14
7200 14 34 247
7200 15 364 14

使用awk

awk 'NR==FNR { sec[$1, $2]; next } !($1, $2) in sec' FS='/' file1 FS=' ' file2
0 14
0 15
0 20
7200 14
7200 15

每個輸入文件之前的FS欄位分隔)定義該文件的欄位分隔符。

最後回答添加的問題:

$ join -v 2 <(sed 's/ /:/' file1) <(sed 's/ /:/' file2) | sed 's/:/ /'
0 14 2 19
0 15 157 67
0 20 28 57
7200 14 34 247
7200 15 364 14

join此答案中的其他變體(提供原始問題的答案)一樣,這確保連接鍵是沒有空格的單個字元串,然後從第二個文件中選擇要連接的行key 與第一個文件中的任何條目都不匹配。

這對必須以相同方式排序的文件做出了相同的假設。就像join一次只在記憶體中保留兩行一樣,我們仍然有相同的好處,grep以及需要將一個文件中的所有條目保留在記憶體中的任何其他解決方案。


使用原始文件file1file2您的問題,將第一個文件tr即時轉換為與第二個文件相同的格式,並使用重新格式化的數據作為一組行從第二個文件中刪除。

$ grep -v -x -F -f <(tr '/' ' ' <file1) file2
0 14
0 15
0 20
7200 14
7200 15

grep實用程序在這裡用於過濾掉(刪除、排除)file2與來自file1.

-x選項強制進行全行匹配(不是通常的子字元串匹配),並將模式用作固定字元串而不是正則表達式-Fgrep-f選項告訴實用程序從命名文件中讀取模式(程序替換),並-v反轉匹配的通常含義,以便輸出匹配的行。

也與您問題中的一些文字相關:


一種更有效的方法是使用join. file1如果你很大,這可能是個好主意。在大量輸入上,這預計會比使用grep.

下面假設您的兩個文件都以相同的方式排序,並將第二個文件轉換為與第一個文件相同的格式(用斜杠替換空格)以生成沒有空格的行。我們以這種方式進行轉換,join預設情況下使用空格作為分隔符,我們需要考慮整行,而不僅僅是第一個空格分隔的欄位。

$ join -v 2 file1 <(tr ' ' '/' <file2) | tr '/' ' ' 
0 14
0 15
0 20
7200 14
7200 15

這將在兩個數據集之間執行關係 JOIN 操作,並將第二個輸入中不匹配的行返回到join(轉換後的第二個文件)。由於我們希望將空格分隔的數據作為最終結果,因此我們將最後的斜杠替換為空格。

這在任何時候都不會在記憶體中保存超過兩行的數據,而grep變體需要將第一個文件的全部內容保留在記憶體中,並且還需要針對第二個文件的每一行測試該文件的每一行文件。

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