- How can I read first n and last n lines from a file?
- 10 Answers 10
- Explanation
- How to get the last line of a file using cat command
- 2 Answers 2
- Related
- Hot Network Questions
- Subscribe to RSS
- How to read first and last line from cat output?
- View files using – cat, more, tail, head and wc commands
- cat Command
- more Command
- head Command
- tail Command
- wc Command
How can I read first n and last n lines from a file?
How can I read the first n lines and the last n lines of a file? For n=2 , I read online that (head -n2 && tail -n2) would work, but it doesn’t.
$ cat x 1 2 3 4 5 $ cat x | (head -n2 && tail -n2) 1 2
Also, the link you sent is not helpful because I do not know the range really. I am looking for a simple solution for this
Interestingly, cat x | (head -n2 && tail -n2) doesn’t work but (head -n2 && tail -n2) < x does. I'll have to meditate a bit on why that is.
What would the expected output be if the input file was 3 lines long? Would it be 1 2 3 or 1 2 2 3 or something else? What if it was only 2 lines long — would the output be 1 2 1 2 or 1 1 2 2 or 1 2 or something else?
I don’t think the head && tail trick is reliable. head from GNU coreutils behaves differently for pipes and regular files (source: the source), reading blockwise in one case but not the other. Depending on implementation details like that seems like a bad idea — it’s not guaranteed that head will leave everything it doesn’t print for tail to work with.
10 Answers 10
head -n2 file && tail -n2 file
This isn’t guaranteed to work even if your file is longer than 4 lines, if a single head buffer is so long enough that there aren’t enough lines left in the file for tail to work.
Chances are you’re going to want something like:
or if you need to specify a number and taking into account @Wintermute’s astute observation that you don’t need to buffer the whole file, something like this is what you really want:
I think the math is correct on that — hopefully you get the idea to use a rotating buffer indexed by the NR modded by the size of the buffer and adjusted to use indices in the range 1-n instead of 0-(n-1).
To help with comprehension of the modulus operator used in the indexing above, here is an example with intermediate print statements to show the logic as it executes:
$ cat tst.awk BEGIN < print "Populating array by index ((NR-1)%n)+1:" > < buf[((NR-1)%n)+1] = $0 printf "NR=%d, n=%d: ((NR-1 = %d) %%n = %d) +1 = %d ->buf[%d] = %s\n", NR, n, NR-1, (NR-1)%n, ((NR-1)%n)+1, ((NR-1)%n)+1, buf[((NR-1)%n)+1] > END < print "\nAccessing array by index ((NR+i-1)%n)+1:" for (i=1;i<=n;i++) < printf "NR=%d, i=%d, n=%d: (((NR+i = %d) - 1 = %d) %%n = %d) +1 = %d ->buf[%d] = %s\n", NR, i, n, NR+i, NR+i-1, (NR+i-1)%n, ((NR+i-1)%n)+1, ((NR+i-1)%n)+1, buf[((NR+i-1)%n)+1] > > $ $ awk -v n=3 -f tst.awk file Populating array by index ((NR-1)%n)+1: NR=1, n=3: ((NR-1 = 0) %n = 0) +1 = 1 -> buf[1] = 1 NR=2, n=3: ((NR-1 = 1) %n = 1) +1 = 2 -> buf[2] = 2 NR=3, n=3: ((NR-1 = 2) %n = 2) +1 = 3 -> buf[3] = 3 NR=4, n=3: ((NR-1 = 3) %n = 0) +1 = 1 -> buf[1] = 4 NR=5, n=3: ((NR-1 = 4) %n = 1) +1 = 2 -> buf[2] = 5 NR=6, n=3: ((NR-1 = 5) %n = 2) +1 = 3 -> buf[3] = 6 NR=7, n=3: ((NR-1 = 6) %n = 0) +1 = 1 -> buf[1] = 7 NR=8, n=3: ((NR-1 = 7) %n = 1) +1 = 2 -> buf[2] = 8 Accessing array by index ((NR+i-1)%n)+1: NR=8, i=1, n=3: (((NR+i = 9) - 1 = 8) %n = 2) +1 = 3 -> buf[3] = 6 NR=8, i=2, n=3: (((NR+i = 10) - 1 = 9) %n = 0) +1 = 1 -> buf[1] = 7 NR=8, i=3, n=3: (((NR+i = 11) - 1 = 10) %n = 1) +1 = 2 -> buf[2] = 8
+1 since this works in a pipe. You might add a more elaborated version which takes files (streams) into account having less then 4 (head+tail) lines..
@EdMorton But it would still need to buffer the whole stream in memory.. (However I don’t see a way without buffering if it should work in a pipe, except saving the stream into a temporary file)
I understand but the bug was just that I was setting ORS=’\n’ when I should have been setting OFS=’\n’ . Now that that’s fixed there’s no need to explicitly hard-code «\n» s between fields.
This might work for you (GNU sed):
This keeps a window of 2 (replace the 2’s for n) lines and then prints the first 2 lines and at end of file prints the window i.e. the last 2 lines.
Here’s a GNU sed one-liner that prints the first 10 and last 10 lines:
If you want to print a ‘—‘ separator between them:
If you’re on a Mac and don’t have GNU sed, you can’t condense as much:
Explanation
gsed -ne’ invoke sed without automatic printing pattern space
-e’1,9′ print the first 9 lines
-e’10’ print line 10 with an appended ‘—‘ separator
-e’:a;$p;N;21,$D;ba’ print the last 10 lines
If you are using a shell that supports process substitution, another way to accomplish this is to write to multiple processes, one for head and one for tail . Suppose for this example your input comes from a pipe feeding you content of unknown length. You want to use just the first 5 lines and the last 10 lines and pass them on to another pipe:
cat | < tee >(head -5) >(tail -10) 1>/dev/null> | cat
The use of <> collects the output from inside the group (there will be two different programs writing to stdout inside the process shells). The 1>/dev/null is to get rid of the extra copy tee will try to write to it’s own stdout.
That demonstrates the concept and all the moving parts, but it can be simplified a little in practice by using the STDOUT stream of tee instead of discarding it. Note the command grouping is still necessary here to pass the output on through the next pipe!
cat | < tee >(head -5) | tail -15 > | cat
Obviously replace cat in the pipeline with whatever you are actually doing. If your input can handle the same content to writing to multiple files you could eliminate the use of tee entirely as well as monkeying with STDOUT. Say you have a command that accepts multiple -o output file name flags:
How to get the last line of a file using cat command
I am writing a shell script in OSX(unix) environment. I have a file called test.properties with the following content: cat test.properties gets the following output:
//This file is intended for //blah blah purposes 123
It’s probable because this is one of the «How do I use X to do Y?» questions where X does not do Y. Such questions are not as useful as «How do I do Y?» or «What does X do?».
// , Those of us who think X will do Y, when, in reality, Z does Y (ahem) do benefit from such questions, in spite of their dubious pedagogy.
2 Answers 2
Don’t use cat . tail was meant for this usecase exactly:
Don’t have enough reputation to comment Mureinik’s post to answer your last question, but if you want to display a part of a file between known lines, you can try sed -n ‘,p .
If you don’t know the lines, mix tail and sed : For your file :
tail -r test.properties | sed -n '2p'
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.7.14.43533
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
How to read first and last line from cat output?
When reading from stdin if would look like this (for example ps -ef ):
ps -ef | sed -e 1b -e '$!d' UID PID PPID C STIME TTY TIME CMD root 1931 1837 0 20:05 pts/0 00:00:00 sed -e 1b -e $!d
head & tail Solution:
When data is coming from a command ( ps -ef ):
ps -ef 2>&1 | (head -n1 && tail -n1) UID PID PPID C STIME TTY TIME CMD root 2068 1837 0 20:13 pts/0 00:00:00 -bash
awk Solution:
And also the piped example with ps -ef :
ps -ef | awk 'NR==1; END' UID PID PPID C STIME TTY TIME CMD root 1935 1837 0 20:07 pts/0 00:00:00 awk NR==1; END
Thank you! It’s the best answer because i cannot do (head -n1 file;tail -n1 file) i have very big command and pipe as last symbol. So | sed ‘1p;$!d’ shorter one.
Sorry for my English, if you don’t understand me — it’s my problem — tell me about it and i prepare better presentation for you.
@DavidConrad, to elaborate on what chaos said, -e 1b — on the first line, branch to the end of the sed script, at which point the implicit «print» happens. If the input is only one line long, sed ends. Otherwise, for all lines except the last, delete. On the last line, the implicit «print» happens again.
@mikeserv: 😉 check it when you get a chance. On a single line it efectively prevents doubling it on output, but on a multiple line work-case, it just preserves the last line. at least on GNU sed 4.2.2. Cheers.
sed -n ‘1p;$p’ file.txt will print 1st and last line of file.txt .
Note that if the input has only one line, it will be printed twice. You may prefer sed -e 1b -e ‘$!d’ if you don’t want that.
cb() < (($1-1>0)) && unset "ary[$1-1]"; > mapfile -t -C cb -c 1 ary < file
After this, you'll have an array ary with first field (i.e., with index 0 ) being the first line of file , and its last field being the last line of file . The callback cb (optional if you want to slurp all lines in the array) unsets all the intermediate lines so as to not clutter memory. As a free by-product, you'll also have the number of lines in the file (as the last index of the array+1).
$ mapfile -t -C cb -c 1 ary < <(printf '%s\n' ) $ declare -p ary declare -a ary='([0]="a" [25]="z")' $ # With only one line $ mapfile -t -C cb -c 1 ary < <(printf '%s\n' "only one line") $ declare -p ary declare -a ary='([0]="only one line")' $ # With an empty file $ mapfile -t -C cb -c 1 ary < <(:) declare -a ary='()'
View files using – cat, more, tail, head and wc commands
There are several commands that display information about a file in the read-only format. The file-viewing commands include the following:
cat Command
The cat command displays the content of one or more text files on the screen without pausing.
# cat data.txt northwest NW Joel Craig 10 western WE Sharon Kelly 40 southwest SW Chris Foster 33 central CT Sheri Watson 44
Do not use the cat command to read binary files. Using the cat command to read binary files can cause a terminal window to freeze. If your terminal window freezes, close the terminal window, and open a new terminal window.
Note: Before you attempt to open a file with the cat command, it is recommended that you first run the file command to determine the file type.
more Command
The more command displays the content of a text file one screen at a time.
message appears at the bottom of each screen, where n% is the percentage of the file that has been displayed. When the entire file has been displayed, the shell prompt appears.
When the –More–(n%)prompt appears at the bottom of the screen, you can use the keys described in the table to scroll through the file.
Keyboard Command | Action |
---|---|
Space bar | Moves forward one screen |
Return | Scrolls one line at a time |
b | Moves back one screen |
h | Displays a help menu of features |
/string | Searches forward for pattern |
n | Finds the next occurrence of pattern |
q | Quits and returns to the shell prompt |
head Command
The head command displays the first 10 lines of a file.
You can change the number of lines displayed by using the -n option. For example, to display the first five lines of the /var/log/messages file, enter the head command with the -n option set to 5.
tail Command
The tail command displays the last 10 lines of a file.
You can change the number of lines displayed by using the -n or +n options.
– The -n option displays n lines from the end of the file.
– The +n option displays the file from line n to the end of the file.
For example, to display the last four lines of the /var/log/messages file, enter the tail command with the -n option set to 4.
For example, to display line 10 through the end of the data.txt file, enter the tail command with the +n option set to 10.
wc Command
The wc command displays the number of lines, words, and characters contained in a file.
You can use the following options with the wc command.
Symbol | Description |
---|---|
-l | Line count |
-w | Word count |
-c | Byte count |
-m | Character count |
When you use the wc command without options, the output displays the number of lines, words, and characters contained in the file. For example, to display the number of lines, words, and characters in the dante file, use the wc command.
$ wc data.txt 32 223 1319 data.txt
For example, to display the number of lines in the dante file, enter the wc command with the -l option.