這是作業,但我不會問具體的作業問題。
我需要使用 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 -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
在這種情況下,這有點矯枉過正,但如果你的過濾器變得更大,它可能會成為一個福音。