Text-Processing

如何使用 sed、grep 或 awk 根據另一個文件中的行號將某些行保留在文件中

  • August 28, 2022

我有兩個文件。File1包含一些句子,並File2包含我要保留的行號File1

例如File1

He is a boy.
She is a cook.
Okay.
She went to school.
She is pretty.

File2

1
4

輸出:

He is a boy.
She went to school.

有沒有辦法使用 , 或 來做到sedgrep一點awk?我不想在這里手動寫行號。

我們可以將數字列表轉換為一系列命令,並在一次呼叫中將sed它們作為編輯腳本執行:sed``sed

sed 's/$/p/' lines.list | sed -n -f /dev/stdin file.txt

在這裡,第一個通過簡單地插入每行的末尾來sed創建一個sed包含諸如 等命令的腳本1p。然後將此腳本發送到管道之後的第二個,管道讀取它並將其與文本文件一起作為輸入應用。4p``p``sed``-f /dev/stdin

這將只需要讀取每個文件一次。


使用awk,將行號作為鍵讀入關聯數組,然後,在讀取另一個文件時,查看目前行號是否是先前在數組中作為鍵的行號之一:

awk 'FNR == NR { lines[$0]; next } (FNR in lines)' lines.list file.txt

awk中,特殊變數NRFNR分別是到目前為止讀取的記錄(行)總數和目前文件中讀取的記錄(行)總數。如果NR等於FNR,我們從第一個輸入文件中讀取,我們使用目前行創建一個數組條目$0,作為鍵(沒有給出值),並立即跳到下一行輸入。

如果我們不是從目前行讀取,我們會測試FNR in lines以查看FNR目前文件中的行號是否是數組中名為lines. 如果是,將列印目前行。


如果沒有其他工具的大力支持,該grep實用程序並不是真正為執行此類任務而設計的。它從內容匹配(或不匹配)給定模式的文本文件中提取行。因此,模式應該與行匹配,而不是行號。

以下內容僅供娛樂,不應被視為如何實際解決此問題的建議。

您可以使用插入行號grep

grep -n '.*' file.txt

這會在文件中所有行的開頭插入行號,緊隨其後:的是行的原始內容。

然後,與sed解決方案一樣,我們可以修改模式文件以使其匹配這些特定數字的選擇:

sed 's/.*/^&:/' lines.list

這將輸出諸如^1:and之類的正則表達式^4:,每個表達式都匹配行首的特定行號。

然後我們可以grep使用這些表達式(這裡借助程序替換)。最後,我們使用以下命令刪除臨時行號cut

grep -n '.*' file.txt | grep -f <(sed 's/.*/^&:/' lines.list) | cut -d : -f 2-

…但這太做作了,甚至不能被認為是合理的解決方案。


上述每個解決方案都將始終按照它們在文本文件中出現的順序顯示選定的行。如果您想按行號文件中出現的順序輸出行,那麼您可以改用ed(或awk,請參閱下文):

sed 's/$/p/' lines.list | ed -s file.txt

同樣,我們從行號文件創建一個編輯腳本,只需p在每行的末尾添加即可。

然後將該腳本作為命令輸入傳遞給ed編輯器,編輯器將命令按順序應用於文本文件。

測試:

$ cat lines.list
4
1
$ sed 's/$/p/' lines.list | ed -s file.txt
She went to school.
He is a boy.

請注意,ed將整個文件讀入記憶體,就像下面的等效awk程序一樣:

awk 'NR == FNR { lines[FNR] = $0; next } { print lines[$0] }' file.txt lines.list

請注意,與以前的awk解決方案相比,輸入文件是切換的。這允許我們首先將文本文件逐行讀取到lines數組中,然後在讀取帶有行號的文件時從中隨機選擇行。

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