使用 head 和 tail 抓取不同的行集並儲存到同一個檔案中

使用 head 和 tail 抓取不同的行集並儲存到同一個檔案中

這是作業,但我不會問具體的作業問題。

我需要使用 head 和 tail 從一個檔案中取得不同的行集。就像第 6-11 行和第 19-24 行一樣,並將它們都儲存到另一個檔案中。我知道我可以使用附加來做到這一點,例如

head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1. 

但我認為我們不應該這樣做。
有沒有特定的方法可以組合 head 和 tail 指令然後儲存到檔案中?

答案1

如果您使用類似的結構對命令進行分組,您可以使用head單獨的基本算術來完成此操作{ ... ; }

{ head -n ...; head -n ...; ...; } < input_file > output_file

所有命令共享相同的輸入(謝謝@mikeserv)。
取得第 6-11 行和第 19-24 行相當於:

head -n 5 >/dev/null  # dump the first 5 lines to `/dev/null` then
head -n 6             # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null  # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6             # then print the next 6 lines (19 up to 24)

所以,基本上,你會運行:

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file

答案2

您可以使用{ … }分組建構將重定向運算子套用至複合命令。

{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1

您可以跳過前 M 行並複製接下來的 N 行,而不是複製前 M+N 行並僅保留最後 N 行。大檔案處理速度明顯更快。請注意,+N的參數tail不是要跳過的行數,而是加一 - 它是要列印的第一行的行號,行號從 1 開始。

{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1

無論哪種方式,輸出檔案僅打開一次,但輸入檔案會遍歷一次以提取每個片段。如何將輸入分組?

{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1

一般來說,這是行不通的。 (它可能在某些系統上工作,至少當輸入是常規文件時。)為什麼?因為輸入緩衝。大多數程式(包括tail)不會逐字節讀取輸入,而是一次讀取幾千字節,因為這樣速度更快。因此tail讀取幾千字節,在開始時跳過一點,再傳遞一點到head,然後停止 - 但讀取的內容是讀取的,並且不可用於下一個命令。

另一種方法是使用head管道/dev/null跳過行。

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1

同樣,由於緩衝,這不能保證有效。head當輸入來自常規檔案時,它恰好可以與 GNU coreutils(非嵌入式 Linux 系統上的命令)中的命令一起使用。那是因為一旦這個實作head讀取了它想要的內容,它設定文件位置到它沒有輸出的第一個位元組。如果輸入是管道,則這不起作用。

從文件中列印多個行序列的一種更簡單的方法是呼叫更通用的工具,例如sed或者awk。 (這可能會比較慢,但這只適用於非常大的文件。)

sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1

答案3

我知道你說過你需要使用 head 和 tail,但 sed 絕對是完成這裡工作的更簡單的工具。

$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1

您甚至可以使用其他進程在字串中建立區塊並透過 sed 運行它。

$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1

-n 否定輸出,然後用 p 指定要列印的範圍,範圍的第一個和最後一個數字用逗號分隔。

話雖這麼說,您可以執行 @don_crissti 建議的命令分組,也可以循環遍歷文件幾次,每次遍歷時頭/尾都會抓取一大塊行。

$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1

檔案中的行越多,區塊越多,sed 的效率就越高。

答案4

使用像這樣的 bash 函數:

seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24

在這種情況下,這有點矯枉過正,但如果你的過濾器變得更大,它可能會成為一個福音。

相關內容