Command to display first few and last few lines of a file
I have a file with many rows, and each row has a timestamp at the starting, like
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
So, I frequently check 2 things from this log file.
- First few rows, that has the global conditions and start time is also given.
- Last few rows, that has the exit status with some other info.
Is there any quick handy single command that could let me display just the first and last few lines of a file?
text-processing logs tail cat head
|
show 3 more comments
I have a file with many rows, and each row has a timestamp at the starting, like
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
So, I frequently check 2 things from this log file.
- First few rows, that has the global conditions and start time is also given.
- Last few rows, that has the exit status with some other info.
Is there any quick handy single command that could let me display just the first and last few lines of a file?
text-processing logs tail cat head
2
What's global conditions, and doesn'thead and tail
works for you?
– daisy
Sep 21 '12 at 9:40
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
@vonbrand Problem is that I don't knowN
– Bernhard
Feb 28 '13 at 14:36
@Bernhard, I'm nosed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.
– vonbrand
Feb 28 '13 at 14:41
|
show 3 more comments
I have a file with many rows, and each row has a timestamp at the starting, like
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
So, I frequently check 2 things from this log file.
- First few rows, that has the global conditions and start time is also given.
- Last few rows, that has the exit status with some other info.
Is there any quick handy single command that could let me display just the first and last few lines of a file?
text-processing logs tail cat head
I have a file with many rows, and each row has a timestamp at the starting, like
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
So, I frequently check 2 things from this log file.
- First few rows, that has the global conditions and start time is also given.
- Last few rows, that has the exit status with some other info.
Is there any quick handy single command that could let me display just the first and last few lines of a file?
text-processing logs tail cat head
text-processing logs tail cat head
edited Feb 28 '13 at 18:50
rahmu
10.3k1969110
10.3k1969110
asked Sep 21 '12 at 9:22
mtkmtk
8,1232762103
8,1232762103
2
What's global conditions, and doesn'thead and tail
works for you?
– daisy
Sep 21 '12 at 9:40
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
@vonbrand Problem is that I don't knowN
– Bernhard
Feb 28 '13 at 14:36
@Bernhard, I'm nosed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.
– vonbrand
Feb 28 '13 at 14:41
|
show 3 more comments
2
What's global conditions, and doesn'thead and tail
works for you?
– daisy
Sep 21 '12 at 9:40
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
@vonbrand Problem is that I don't knowN
– Bernhard
Feb 28 '13 at 14:36
@Bernhard, I'm nosed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.
– vonbrand
Feb 28 '13 at 14:41
2
2
What's global conditions, and doesn't
head and tail
works for you?– daisy
Sep 21 '12 at 9:40
What's global conditions, and doesn't
head and tail
works for you?– daisy
Sep 21 '12 at 9:40
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
@vonbrand Problem is that I don't know
N
– Bernhard
Feb 28 '13 at 14:36
@vonbrand Problem is that I don't know
N
– Bernhard
Feb 28 '13 at 14:36
@Bernhard, I'm no
sed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.– vonbrand
Feb 28 '13 at 14:41
@Bernhard, I'm no
sed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.– vonbrand
Feb 28 '13 at 14:41
|
show 3 more comments
9 Answers
9
active
oldest
votes
You can use sed
or awk
to make it with one command. However you'll loose at speed, cause sed
and awk
will need to run through the whole file anyway.
From a speed point of view it's much better to make a function or every time to combination of tail
+ head
. This does have the downside of not working if the input is a pipe, however you can use proccess substitution, in case your shell supports it (look at example below).
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
and just launch it as
first_last "/path/to/file_to_process"
to proceed with process substitution (bash, zsh, ksh like shells only):
first_last <( command )
ps. you can even add a grep
to check if your "global conditions" exist.
-n 10
is the default, no?
– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.-n 10
is not necessary here.
– rush
Mar 1 '13 at 10:35
add a comment |
@rush is right about using head + tail being more efficient for large files, but for small files (< 20 lines), some lines may be output twice.
{ head; tail;} < /path/to/file
would be equally efficient, but wouldn't have the problem above.
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
@Gilles I missed the space:{head; tail;} < file
works in zsh but fails in sh.{ head; tail;} < file
always works. Sorry for the noise.
– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be withhead
, not the shell. POSIX requireshead
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIXhead
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).
– Stéphane Chazelas
Feb 28 '13 at 15:41
1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
|
show 3 more comments
The { head; tail; }
solution wouldn't work on pipes (or sockets or any other non-seekable files) because head
could consume too much data as it reads by blocks and can't seek back on a pipe potentially leaving the cursor inside the file beyond what tail
is meant to select.
So, you could use a tool that reads one character at a time like the shell's read
(here using a function that takes the number of head lines and tail lines as arguments).
head_tail() {
n=0
while [ "$n" -lt "$1" ]; do
IFS= read -r line || { printf %s "$line"; break; }
printf '%sn' "$line"
n=$(($n + 1))
done
tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5
or implement tail
in awk for instance as:
head_tail() {
awk -v h="$1" -v t="${2-$1}" '
{l[NR%t]=$0}
NR<=h
END{
n=NR-t+1
if(n <= h) n = h+1
for (;n<=NR;n++) print l[n%t]
}'
}
With sed
:
head_tail() {
sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),$!{N;b1" -e '}' -e 'N;D'
}
(though beware that some sed
implementations have a low limitation on the size of their pattern space, so would fail for big values of the number of tail lines).
add a comment |
Using bash
process substitution, you can do the following:
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
Note that the lines are not guaranteed to be in order, though for files longer than about 8kB, they very likely will be. This 8kB cutoff is the typical size of the read buffer, and is related to the reason | {head; tail;}
doesn't work for small files.
The cat >/dev/null
is necessary to keep the head
pipeline alive. Otherwise tee
will quit early, and while you'll get output from tail
, it'll be from somewhere in the middle of the input, rather than the end.
Finally, why the >/dev/null
instead of, say, moving tail
to another |
? In the following case:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
's stdout is fed into the pipe to tail
rather than the console, which isn't what we want at all.
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, becausetail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.
– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE withtee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages fromtee
.
– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),tail
is the one being killed by SIGPIPE, nottee
, andtail
isn't writing to a pipe. So it must be from akill()
, right?. And this only happens when I'm using the|
syntax.strace
says thattee
isn't callingkill()
... so maybebash
?
– Jander
Feb 28 '13 at 16:38
1
@Jander, try feeding more than 8k likeseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
|
show 3 more comments
Using ed
(which will read the entire file into RAM though):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%sn' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
Shorter:ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
add a comment |
Stephane's first solution in a function so that you can use arguments (works in any Bourne-like or POSIX shell):
head_tail() {
head "$@";
tail "$@";
}
Now you can do this:
head_tail -n 5 < /path/to/file
This of course assumes that you're looking at only one file and like Stephane's solution works (reliably) only on regular (seekable) files.
add a comment |
With the -u
(--unbuffered
) option of GNU sed
, you can use sed -u 2q
as an unbuffered alternative to head -n2
:
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
(head -n2;tail -n2)
fails when the last lines are part of the block of the input that is consumed by head
:
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
add a comment |
I ran into something like this today where I needed only the last line and a few lines from the front of a stream and came up with the following.
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
I read this as: initialize the hold space with the contents of the first line, append lines 2-3 in the hold space, at EOF append the last line to the hold space, swap hold-and-pattern space, and print the pattern space.
Perhaps someone with more sed
-fu than I have can figure out how to generalize this to print the last few lines of the stream indicated in this question but I didn't need it and could not find an easy way to do math based on the $
address in sed
or perhaps by managing the hold space so that only the last few lines are in it when EOF
is reached.
add a comment |
You may try Perl, if you have it installed:
perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
This will work for most files, but reads the whole file into memory before processing it. If you're not familiar with Perl slices, "0" in square brackets means "take the first line", and "-3...-1" means "take last three lines". You may tailor both of them to your needs. If you need to process really large files (what is 'large' may depend on your RAM and perhaps swap sizes), you may want to go for:
perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
it may be somewhat slower, because it makes a slice every iteration, but it's independent on the file size.
Both commands should work both in pipes and with regular files.
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f48777%2fcommand-to-display-first-few-and-last-few-lines-of-a-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
9 Answers
9
active
oldest
votes
9 Answers
9
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can use sed
or awk
to make it with one command. However you'll loose at speed, cause sed
and awk
will need to run through the whole file anyway.
From a speed point of view it's much better to make a function or every time to combination of tail
+ head
. This does have the downside of not working if the input is a pipe, however you can use proccess substitution, in case your shell supports it (look at example below).
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
and just launch it as
first_last "/path/to/file_to_process"
to proceed with process substitution (bash, zsh, ksh like shells only):
first_last <( command )
ps. you can even add a grep
to check if your "global conditions" exist.
-n 10
is the default, no?
– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.-n 10
is not necessary here.
– rush
Mar 1 '13 at 10:35
add a comment |
You can use sed
or awk
to make it with one command. However you'll loose at speed, cause sed
and awk
will need to run through the whole file anyway.
From a speed point of view it's much better to make a function or every time to combination of tail
+ head
. This does have the downside of not working if the input is a pipe, however you can use proccess substitution, in case your shell supports it (look at example below).
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
and just launch it as
first_last "/path/to/file_to_process"
to proceed with process substitution (bash, zsh, ksh like shells only):
first_last <( command )
ps. you can even add a grep
to check if your "global conditions" exist.
-n 10
is the default, no?
– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.-n 10
is not necessary here.
– rush
Mar 1 '13 at 10:35
add a comment |
You can use sed
or awk
to make it with one command. However you'll loose at speed, cause sed
and awk
will need to run through the whole file anyway.
From a speed point of view it's much better to make a function or every time to combination of tail
+ head
. This does have the downside of not working if the input is a pipe, however you can use proccess substitution, in case your shell supports it (look at example below).
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
and just launch it as
first_last "/path/to/file_to_process"
to proceed with process substitution (bash, zsh, ksh like shells only):
first_last <( command )
ps. you can even add a grep
to check if your "global conditions" exist.
You can use sed
or awk
to make it with one command. However you'll loose at speed, cause sed
and awk
will need to run through the whole file anyway.
From a speed point of view it's much better to make a function or every time to combination of tail
+ head
. This does have the downside of not working if the input is a pipe, however you can use proccess substitution, in case your shell supports it (look at example below).
first_last () {
head -n 10 -- "$1"
tail -n 10 -- "$1"
}
and just launch it as
first_last "/path/to/file_to_process"
to proceed with process substitution (bash, zsh, ksh like shells only):
first_last <( command )
ps. you can even add a grep
to check if your "global conditions" exist.
edited Mar 1 '13 at 10:33
answered Sep 21 '12 at 9:44
rushrush
19.1k46395
19.1k46395
-n 10
is the default, no?
– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.-n 10
is not necessary here.
– rush
Mar 1 '13 at 10:35
add a comment |
-n 10
is the default, no?
– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.-n 10
is not necessary here.
– rush
Mar 1 '13 at 10:35
-n 10
is the default, no?– l0b0
Mar 1 '13 at 9:54
-n 10
is the default, no?– l0b0
Mar 1 '13 at 9:54
@l0b0 yes, it's default.
-n 10
is not necessary here.– rush
Mar 1 '13 at 10:35
@l0b0 yes, it's default.
-n 10
is not necessary here.– rush
Mar 1 '13 at 10:35
add a comment |
@rush is right about using head + tail being more efficient for large files, but for small files (< 20 lines), some lines may be output twice.
{ head; tail;} < /path/to/file
would be equally efficient, but wouldn't have the problem above.
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
@Gilles I missed the space:{head; tail;} < file
works in zsh but fails in sh.{ head; tail;} < file
always works. Sorry for the noise.
– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be withhead
, not the shell. POSIX requireshead
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIXhead
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).
– Stéphane Chazelas
Feb 28 '13 at 15:41
1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
|
show 3 more comments
@rush is right about using head + tail being more efficient for large files, but for small files (< 20 lines), some lines may be output twice.
{ head; tail;} < /path/to/file
would be equally efficient, but wouldn't have the problem above.
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
@Gilles I missed the space:{head; tail;} < file
works in zsh but fails in sh.{ head; tail;} < file
always works. Sorry for the noise.
– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be withhead
, not the shell. POSIX requireshead
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIXhead
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).
– Stéphane Chazelas
Feb 28 '13 at 15:41
1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
|
show 3 more comments
@rush is right about using head + tail being more efficient for large files, but for small files (< 20 lines), some lines may be output twice.
{ head; tail;} < /path/to/file
would be equally efficient, but wouldn't have the problem above.
@rush is right about using head + tail being more efficient for large files, but for small files (< 20 lines), some lines may be output twice.
{ head; tail;} < /path/to/file
would be equally efficient, but wouldn't have the problem above.
edited Feb 28 '13 at 14:49
jofel
20.1k34780
20.1k34780
answered Sep 21 '12 at 12:02
Stéphane ChazelasStéphane Chazelas
301k55564916
301k55564916
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
@Gilles I missed the space:{head; tail;} < file
works in zsh but fails in sh.{ head; tail;} < file
always works. Sorry for the noise.
– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be withhead
, not the shell. POSIX requireshead
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIXhead
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).
– Stéphane Chazelas
Feb 28 '13 at 15:41
1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
|
show 3 more comments
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
@Gilles I missed the space:{head; tail;} < file
works in zsh but fails in sh.{ head; tail;} < file
always works. Sorry for the noise.
– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be withhead
, not the shell. POSIX requireshead
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIXhead
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).
– Stéphane Chazelas
Feb 28 '13 at 15:41
1
@FCTW,sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
In contrast to rushs solution, this does not work in a POSIX shell.
– Marco
Feb 28 '13 at 14:51
2
2
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
@Marco Huh? Only POSIX constructs are used here. What do you see going wrong?
– Gilles
Feb 28 '13 at 15:10
2
2
@Gilles I missed the space:
{head; tail;} < file
works in zsh but fails in sh. { head; tail;} < file
always works. Sorry for the noise.– Marco
Feb 28 '13 at 15:39
@Gilles I missed the space:
{head; tail;} < file
works in zsh but fails in sh. { head; tail;} < file
always works. Sorry for the noise.– Marco
Feb 28 '13 at 15:39
@Marco, if there were problems with that, it would be with
head
, not the shell. POSIX requires head
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIX head
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).– Stéphane Chazelas
Feb 28 '13 at 15:41
@Marco, if there were problems with that, it would be with
head
, not the shell. POSIX requires head
to leave the cursor in the file just past those 10 lines for regular files. A problem could arise for non-POSIX head
implementations (very old versions of GNU head used to be non-conformant in that instance, but we're talking decades) or if the file is not seekable (like named pipe or socket, but then the other solution would have the same problem).– Stéphane Chazelas
Feb 28 '13 at 15:41
1
1
@FCTW,
sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
@FCTW,
sudo sh -c '{ head; tail;} < /path/to/file'
– Stéphane Chazelas
Mar 10 '16 at 15:08
|
show 3 more comments
The { head; tail; }
solution wouldn't work on pipes (or sockets or any other non-seekable files) because head
could consume too much data as it reads by blocks and can't seek back on a pipe potentially leaving the cursor inside the file beyond what tail
is meant to select.
So, you could use a tool that reads one character at a time like the shell's read
(here using a function that takes the number of head lines and tail lines as arguments).
head_tail() {
n=0
while [ "$n" -lt "$1" ]; do
IFS= read -r line || { printf %s "$line"; break; }
printf '%sn' "$line"
n=$(($n + 1))
done
tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5
or implement tail
in awk for instance as:
head_tail() {
awk -v h="$1" -v t="${2-$1}" '
{l[NR%t]=$0}
NR<=h
END{
n=NR-t+1
if(n <= h) n = h+1
for (;n<=NR;n++) print l[n%t]
}'
}
With sed
:
head_tail() {
sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),$!{N;b1" -e '}' -e 'N;D'
}
(though beware that some sed
implementations have a low limitation on the size of their pattern space, so would fail for big values of the number of tail lines).
add a comment |
The { head; tail; }
solution wouldn't work on pipes (or sockets or any other non-seekable files) because head
could consume too much data as it reads by blocks and can't seek back on a pipe potentially leaving the cursor inside the file beyond what tail
is meant to select.
So, you could use a tool that reads one character at a time like the shell's read
(here using a function that takes the number of head lines and tail lines as arguments).
head_tail() {
n=0
while [ "$n" -lt "$1" ]; do
IFS= read -r line || { printf %s "$line"; break; }
printf '%sn' "$line"
n=$(($n + 1))
done
tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5
or implement tail
in awk for instance as:
head_tail() {
awk -v h="$1" -v t="${2-$1}" '
{l[NR%t]=$0}
NR<=h
END{
n=NR-t+1
if(n <= h) n = h+1
for (;n<=NR;n++) print l[n%t]
}'
}
With sed
:
head_tail() {
sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),$!{N;b1" -e '}' -e 'N;D'
}
(though beware that some sed
implementations have a low limitation on the size of their pattern space, so would fail for big values of the number of tail lines).
add a comment |
The { head; tail; }
solution wouldn't work on pipes (or sockets or any other non-seekable files) because head
could consume too much data as it reads by blocks and can't seek back on a pipe potentially leaving the cursor inside the file beyond what tail
is meant to select.
So, you could use a tool that reads one character at a time like the shell's read
(here using a function that takes the number of head lines and tail lines as arguments).
head_tail() {
n=0
while [ "$n" -lt "$1" ]; do
IFS= read -r line || { printf %s "$line"; break; }
printf '%sn' "$line"
n=$(($n + 1))
done
tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5
or implement tail
in awk for instance as:
head_tail() {
awk -v h="$1" -v t="${2-$1}" '
{l[NR%t]=$0}
NR<=h
END{
n=NR-t+1
if(n <= h) n = h+1
for (;n<=NR;n++) print l[n%t]
}'
}
With sed
:
head_tail() {
sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),$!{N;b1" -e '}' -e 'N;D'
}
(though beware that some sed
implementations have a low limitation on the size of their pattern space, so would fail for big values of the number of tail lines).
The { head; tail; }
solution wouldn't work on pipes (or sockets or any other non-seekable files) because head
could consume too much data as it reads by blocks and can't seek back on a pipe potentially leaving the cursor inside the file beyond what tail
is meant to select.
So, you could use a tool that reads one character at a time like the shell's read
(here using a function that takes the number of head lines and tail lines as arguments).
head_tail() {
n=0
while [ "$n" -lt "$1" ]; do
IFS= read -r line || { printf %s "$line"; break; }
printf '%sn' "$line"
n=$(($n + 1))
done
tail -n "${2-$1}"
}
seq 100 | head_tail 5 10
seq 20 | head_tail 5
or implement tail
in awk for instance as:
head_tail() {
awk -v h="$1" -v t="${2-$1}" '
{l[NR%t]=$0}
NR<=h
END{
n=NR-t+1
if(n <= h) n = h+1
for (;n<=NR;n++) print l[n%t]
}'
}
With sed
:
head_tail() {
sed -e "1,${1}b" -e :1 -e "$(($1+${2-$1})),$!{N;b1" -e '}' -e 'N;D'
}
(though beware that some sed
implementations have a low limitation on the size of their pattern space, so would fail for big values of the number of tail lines).
edited Mar 1 '13 at 7:33
answered Feb 28 '13 at 16:01
Stéphane ChazelasStéphane Chazelas
301k55564916
301k55564916
add a comment |
add a comment |
Using bash
process substitution, you can do the following:
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
Note that the lines are not guaranteed to be in order, though for files longer than about 8kB, they very likely will be. This 8kB cutoff is the typical size of the read buffer, and is related to the reason | {head; tail;}
doesn't work for small files.
The cat >/dev/null
is necessary to keep the head
pipeline alive. Otherwise tee
will quit early, and while you'll get output from tail
, it'll be from somewhere in the middle of the input, rather than the end.
Finally, why the >/dev/null
instead of, say, moving tail
to another |
? In the following case:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
's stdout is fed into the pipe to tail
rather than the console, which isn't what we want at all.
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, becausetail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.
– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE withtee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages fromtee
.
– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),tail
is the one being killed by SIGPIPE, nottee
, andtail
isn't writing to a pipe. So it must be from akill()
, right?. And this only happens when I'm using the|
syntax.strace
says thattee
isn't callingkill()
... so maybebash
?
– Jander
Feb 28 '13 at 16:38
1
@Jander, try feeding more than 8k likeseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
|
show 3 more comments
Using bash
process substitution, you can do the following:
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
Note that the lines are not guaranteed to be in order, though for files longer than about 8kB, they very likely will be. This 8kB cutoff is the typical size of the read buffer, and is related to the reason | {head; tail;}
doesn't work for small files.
The cat >/dev/null
is necessary to keep the head
pipeline alive. Otherwise tee
will quit early, and while you'll get output from tail
, it'll be from somewhere in the middle of the input, rather than the end.
Finally, why the >/dev/null
instead of, say, moving tail
to another |
? In the following case:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
's stdout is fed into the pipe to tail
rather than the console, which isn't what we want at all.
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, becausetail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.
– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE withtee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages fromtee
.
– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),tail
is the one being killed by SIGPIPE, nottee
, andtail
isn't writing to a pipe. So it must be from akill()
, right?. And this only happens when I'm using the|
syntax.strace
says thattee
isn't callingkill()
... so maybebash
?
– Jander
Feb 28 '13 at 16:38
1
@Jander, try feeding more than 8k likeseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
|
show 3 more comments
Using bash
process substitution, you can do the following:
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
Note that the lines are not guaranteed to be in order, though for files longer than about 8kB, they very likely will be. This 8kB cutoff is the typical size of the read buffer, and is related to the reason | {head; tail;}
doesn't work for small files.
The cat >/dev/null
is necessary to keep the head
pipeline alive. Otherwise tee
will quit early, and while you'll get output from tail
, it'll be from somewhere in the middle of the input, rather than the end.
Finally, why the >/dev/null
instead of, say, moving tail
to another |
? In the following case:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
's stdout is fed into the pipe to tail
rather than the console, which isn't what we want at all.
Using bash
process substitution, you can do the following:
make_some_output | tee >(tail -n 2) >(head -n 2; cat >/dev/null) >/dev/null
Note that the lines are not guaranteed to be in order, though for files longer than about 8kB, they very likely will be. This 8kB cutoff is the typical size of the read buffer, and is related to the reason | {head; tail;}
doesn't work for small files.
The cat >/dev/null
is necessary to keep the head
pipeline alive. Otherwise tee
will quit early, and while you'll get output from tail
, it'll be from somewhere in the middle of the input, rather than the end.
Finally, why the >/dev/null
instead of, say, moving tail
to another |
? In the following case:
make_some_output | tee >(head -n 2; cat >/dev/null) | tail -n 2 # doesn't work
head
's stdout is fed into the pipe to tail
rather than the console, which isn't what we want at all.
edited Feb 28 '13 at 19:02
answered Feb 28 '13 at 16:07
JanderJander
11.5k43356
11.5k43356
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, becausetail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.
– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE withtee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages fromtee
.
– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),tail
is the one being killed by SIGPIPE, nottee
, andtail
isn't writing to a pipe. So it must be from akill()
, right?. And this only happens when I'm using the|
syntax.strace
says thattee
isn't callingkill()
... so maybebash
?
– Jander
Feb 28 '13 at 16:38
1
@Jander, try feeding more than 8k likeseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
|
show 3 more comments
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, becausetail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.
– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE withtee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages fromtee
.
– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),tail
is the one being killed by SIGPIPE, nottee
, andtail
isn't writing to a pipe. So it must be from akill()
, right?. And this only happens when I'm using the|
syntax.strace
says thattee
isn't callingkill()
... so maybebash
?
– Jander
Feb 28 '13 at 16:38
1
@Jander, try feeding more than 8k likeseq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
When head or tail finish writing the output they want, they close their stdin and exit. That's where the SIGPIPE is coming from. Normally this is a good thing, they're discarding the rest of the output, so there is no reason for the other side of the pipe to continue spending time generating it.
– derobert
Feb 28 '13 at 16:12
What makes the order likely to be upheld? It probably will be for a large file, because
tail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.– Gilles
Feb 28 '13 at 16:20
What makes the order likely to be upheld? It probably will be for a large file, because
tail
has to work longer, but I expect (and do see) it failing about half the time for short inputs.– Gilles
Feb 28 '13 at 16:20
You'll get the SIGPIPE with
tee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages from tee
.– Stéphane Chazelas
Feb 28 '13 at 16:28
You'll get the SIGPIPE with
tee >(head) >(tail)
for the same reasons (>(...)
which by the way is a ksh feature now supported by both zsh and bash as well) uses pipes as well. You could do ... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
but you'll still see some broken pipes error messages from tee
.– Stéphane Chazelas
Feb 28 '13 at 16:28
On my system (bash 4.2.37, coreutils 8.13),
tail
is the one being killed by SIGPIPE, not tee
, and tail
isn't writing to a pipe. So it must be from a kill()
, right?. And this only happens when I'm using the |
syntax. strace
says that tee
isn't calling kill()
... so maybe bash
?– Jander
Feb 28 '13 at 16:38
On my system (bash 4.2.37, coreutils 8.13),
tail
is the one being killed by SIGPIPE, not tee
, and tail
isn't writing to a pipe. So it must be from a kill()
, right?. And this only happens when I'm using the |
syntax. strace
says that tee
isn't calling kill()
... so maybe bash
?– Jander
Feb 28 '13 at 16:38
1
1
@Jander, try feeding more than 8k like
seq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
@Jander, try feeding more than 8k like
seq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
– Stéphane Chazelas
Feb 28 '13 at 17:39
|
show 3 more comments
Using ed
(which will read the entire file into RAM though):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%sn' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
Shorter:ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
add a comment |
Using ed
(which will read the entire file into RAM though):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%sn' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
Shorter:ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
add a comment |
Using ed
(which will read the entire file into RAM though):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%sn' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
Using ed
(which will read the entire file into RAM though):
# cf. http://wiki.bash-hackers.org/howto/edit-ed
printf '%sn' 'H' '1,10p' '$-10,$p' 'q' | ed -s file
answered Mar 1 '13 at 16:17
curxcurx
311
311
Shorter:ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
add a comment |
Shorter:ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
Shorter:
ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
Shorter:
ed -s file <<< $'11,$-10dn,pnqn'
– don_crissti
Dec 20 '14 at 2:18
add a comment |
Stephane's first solution in a function so that you can use arguments (works in any Bourne-like or POSIX shell):
head_tail() {
head "$@";
tail "$@";
}
Now you can do this:
head_tail -n 5 < /path/to/file
This of course assumes that you're looking at only one file and like Stephane's solution works (reliably) only on regular (seekable) files.
add a comment |
Stephane's first solution in a function so that you can use arguments (works in any Bourne-like or POSIX shell):
head_tail() {
head "$@";
tail "$@";
}
Now you can do this:
head_tail -n 5 < /path/to/file
This of course assumes that you're looking at only one file and like Stephane's solution works (reliably) only on regular (seekable) files.
add a comment |
Stephane's first solution in a function so that you can use arguments (works in any Bourne-like or POSIX shell):
head_tail() {
head "$@";
tail "$@";
}
Now you can do this:
head_tail -n 5 < /path/to/file
This of course assumes that you're looking at only one file and like Stephane's solution works (reliably) only on regular (seekable) files.
Stephane's first solution in a function so that you can use arguments (works in any Bourne-like or POSIX shell):
head_tail() {
head "$@";
tail "$@";
}
Now you can do this:
head_tail -n 5 < /path/to/file
This of course assumes that you're looking at only one file and like Stephane's solution works (reliably) only on regular (seekable) files.
edited Mar 1 '13 at 6:27
Stéphane Chazelas
301k55564916
301k55564916
answered Feb 28 '13 at 16:21
l0b0l0b0
27.7k17117243
27.7k17117243
add a comment |
add a comment |
With the -u
(--unbuffered
) option of GNU sed
, you can use sed -u 2q
as an unbuffered alternative to head -n2
:
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
(head -n2;tail -n2)
fails when the last lines are part of the block of the input that is consumed by head
:
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
add a comment |
With the -u
(--unbuffered
) option of GNU sed
, you can use sed -u 2q
as an unbuffered alternative to head -n2
:
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
(head -n2;tail -n2)
fails when the last lines are part of the block of the input that is consumed by head
:
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
add a comment |
With the -u
(--unbuffered
) option of GNU sed
, you can use sed -u 2q
as an unbuffered alternative to head -n2
:
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
(head -n2;tail -n2)
fails when the last lines are part of the block of the input that is consumed by head
:
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
With the -u
(--unbuffered
) option of GNU sed
, you can use sed -u 2q
as an unbuffered alternative to head -n2
:
$ seq 100|(sed -u 2q;tail -n2)
1
2
99
100
(head -n2;tail -n2)
fails when the last lines are part of the block of the input that is consumed by head
:
$ seq 1000|(head -n2;tail -n2)
1
2
999
1000
$ seq 100|(head -n2;tail -n2)
1
2
edited Jan 4 at 11:51
answered Apr 25 '15 at 19:48
nisetamanisetama
55946
55946
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
add a comment |
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
this should be the top answer! works like a charm!
– Ben Usman
Aug 10 '17 at 19:45
add a comment |
I ran into something like this today where I needed only the last line and a few lines from the front of a stream and came up with the following.
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
I read this as: initialize the hold space with the contents of the first line, append lines 2-3 in the hold space, at EOF append the last line to the hold space, swap hold-and-pattern space, and print the pattern space.
Perhaps someone with more sed
-fu than I have can figure out how to generalize this to print the last few lines of the stream indicated in this question but I didn't need it and could not find an easy way to do math based on the $
address in sed
or perhaps by managing the hold space so that only the last few lines are in it when EOF
is reached.
add a comment |
I ran into something like this today where I needed only the last line and a few lines from the front of a stream and came up with the following.
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
I read this as: initialize the hold space with the contents of the first line, append lines 2-3 in the hold space, at EOF append the last line to the hold space, swap hold-and-pattern space, and print the pattern space.
Perhaps someone with more sed
-fu than I have can figure out how to generalize this to print the last few lines of the stream indicated in this question but I didn't need it and could not find an easy way to do math based on the $
address in sed
or perhaps by managing the hold space so that only the last few lines are in it when EOF
is reached.
add a comment |
I ran into something like this today where I needed only the last line and a few lines from the front of a stream and came up with the following.
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
I read this as: initialize the hold space with the contents of the first line, append lines 2-3 in the hold space, at EOF append the last line to the hold space, swap hold-and-pattern space, and print the pattern space.
Perhaps someone with more sed
-fu than I have can figure out how to generalize this to print the last few lines of the stream indicated in this question but I didn't need it and could not find an easy way to do math based on the $
address in sed
or perhaps by managing the hold space so that only the last few lines are in it when EOF
is reached.
I ran into something like this today where I needed only the last line and a few lines from the front of a stream and came up with the following.
sed -n -e '1{h}' -e '2,3{H}' -e '${H;x;p}'
I read this as: initialize the hold space with the contents of the first line, append lines 2-3 in the hold space, at EOF append the last line to the hold space, swap hold-and-pattern space, and print the pattern space.
Perhaps someone with more sed
-fu than I have can figure out how to generalize this to print the last few lines of the stream indicated in this question but I didn't need it and could not find an easy way to do math based on the $
address in sed
or perhaps by managing the hold space so that only the last few lines are in it when EOF
is reached.
answered Mar 14 '14 at 7:31
deaksdeaks
313
313
add a comment |
add a comment |
You may try Perl, if you have it installed:
perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
This will work for most files, but reads the whole file into memory before processing it. If you're not familiar with Perl slices, "0" in square brackets means "take the first line", and "-3...-1" means "take last three lines". You may tailor both of them to your needs. If you need to process really large files (what is 'large' may depend on your RAM and perhaps swap sizes), you may want to go for:
perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
it may be somewhat slower, because it makes a slice every iteration, but it's independent on the file size.
Both commands should work both in pipes and with regular files.
add a comment |
You may try Perl, if you have it installed:
perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
This will work for most files, but reads the whole file into memory before processing it. If you're not familiar with Perl slices, "0" in square brackets means "take the first line", and "-3...-1" means "take last three lines". You may tailor both of them to your needs. If you need to process really large files (what is 'large' may depend on your RAM and perhaps swap sizes), you may want to go for:
perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
it may be somewhat slower, because it makes a slice every iteration, but it's independent on the file size.
Both commands should work both in pipes and with regular files.
add a comment |
You may try Perl, if you have it installed:
perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
This will work for most files, but reads the whole file into memory before processing it. If you're not familiar with Perl slices, "0" in square brackets means "take the first line", and "-3...-1" means "take last three lines". You may tailor both of them to your needs. If you need to process really large files (what is 'large' may depend on your RAM and perhaps swap sizes), you may want to go for:
perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
it may be somewhat slower, because it makes a slice every iteration, but it's independent on the file size.
Both commands should work both in pipes and with regular files.
You may try Perl, if you have it installed:
perl -e '@_ = <>; @_=@_[0, -3..-1]; print @_'
This will work for most files, but reads the whole file into memory before processing it. If you're not familiar with Perl slices, "0" in square brackets means "take the first line", and "-3...-1" means "take last three lines". You may tailor both of them to your needs. If you need to process really large files (what is 'large' may depend on your RAM and perhaps swap sizes), you may want to go for:
perl -e 'while($_=<>){@_=(@_,$_)[0,-3..-1]}; print @_'
it may be somewhat slower, because it makes a slice every iteration, but it's independent on the file size.
Both commands should work both in pipes and with regular files.
edited May 31 '15 at 17:21
serenesat
9181519
9181519
answered Nov 22 '13 at 18:34
JasioJasio
666
666
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f48777%2fcommand-to-display-first-few-and-last-few-lines-of-a-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
What's global conditions, and doesn't
head and tail
works for you?– daisy
Sep 21 '12 at 9:40
That is the part of my log file. I was trying to be elaborative. You can ignore that.
– mtk
Sep 21 '12 at 10:00
Your solution looks fine to me. If you want more convenience, make it into a shell function (even an alias might do).
– vonbrand
Feb 28 '13 at 14:33
@vonbrand Problem is that I don't know
N
– Bernhard
Feb 28 '13 at 14:36
@Bernhard, I'm no
sed(1)
expert, but there are ways of stashing stuff away for later use with it. Maybe it pays off to look in there. OTOH, I'd probably whip up a Perl (or whatever) script to do it if used frequently, as I'm more familiar with that.– vonbrand
Feb 28 '13 at 14:41