Command to display first few and last few lines of a file












23















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.




  1. First few rows, that has the global conditions and start time is also given.

  2. 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?










share|improve this question




















  • 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
















23















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.




  1. First few rows, that has the global conditions and start time is also given.

  2. 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?










share|improve this question




















  • 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














23












23








23


5






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.




  1. First few rows, that has the global conditions and start time is also given.

  2. 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?










share|improve this question
















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.




  1. First few rows, that has the global conditions and start time is also given.

  2. 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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'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














  • 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








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










9 Answers
9






active

oldest

votes


















12














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.






share|improve this answer


























  • -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



















20














@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.






share|improve this answer


























  • 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 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





    @FCTW, sudo sh -c '{ head; tail;} < /path/to/file'

    – Stéphane Chazelas
    Mar 10 '16 at 15:08



















8














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).






share|improve this answer

































    4














    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.






    share|improve this answer


























    • 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











    • 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






    • 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



















    3














    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





    share|improve this answer
























    • Shorter: ed -s file <<< $'11,$-10dn,pnqn'

      – don_crissti
      Dec 20 '14 at 2:18



















    2














    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.






    share|improve this answer

































      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





      share|improve this answer


























      • this should be the top answer! works like a charm!

        – Ben Usman
        Aug 10 '17 at 19:45



















      1














      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.






      share|improve this answer































        1














        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.






        share|improve this answer

























          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          12














          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.






          share|improve this answer


























          • -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
















          12














          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.






          share|improve this answer


























          • -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














          12












          12








          12







          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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



















          • -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













          20














          @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.






          share|improve this answer


























          • 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 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





            @FCTW, sudo sh -c '{ head; tail;} < /path/to/file'

            – Stéphane Chazelas
            Mar 10 '16 at 15:08
















          20














          @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.






          share|improve this answer


























          • 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 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





            @FCTW, sudo sh -c '{ head; tail;} < /path/to/file'

            – Stéphane Chazelas
            Mar 10 '16 at 15:08














          20












          20








          20







          @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.






          share|improve this answer















          @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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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 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





            @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






          • 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 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





            @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











          8














          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).






          share|improve this answer






























            8














            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).






            share|improve this answer




























              8












              8








              8







              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).






              share|improve this answer















              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).







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Mar 1 '13 at 7:33

























              answered Feb 28 '13 at 16:01









              Stéphane ChazelasStéphane Chazelas

              301k55564916




              301k55564916























                  4














                  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.






                  share|improve this answer


























                  • 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











                  • 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






                  • 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
















                  4














                  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.






                  share|improve this answer


























                  • 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











                  • 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






                  • 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














                  4












                  4








                  4







                  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.






                  share|improve this answer















                  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.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  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, 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











                  • 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





                    @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



















                  • 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











                  • 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






                  • 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

















                  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











                  3














                  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





                  share|improve this answer
























                  • Shorter: ed -s file <<< $'11,$-10dn,pnqn'

                    – don_crissti
                    Dec 20 '14 at 2:18
















                  3














                  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





                  share|improve this answer
























                  • Shorter: ed -s file <<< $'11,$-10dn,pnqn'

                    – don_crissti
                    Dec 20 '14 at 2:18














                  3












                  3








                  3







                  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





                  share|improve this answer













                  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






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  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



















                  • 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











                  2














                  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.






                  share|improve this answer






























                    2














                    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.






                    share|improve this answer




























                      2












                      2








                      2







                      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.






                      share|improve this answer















                      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.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Mar 1 '13 at 6:27









                      Stéphane Chazelas

                      301k55564916




                      301k55564916










                      answered Feb 28 '13 at 16:21









                      l0b0l0b0

                      27.7k17117243




                      27.7k17117243























                          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





                          share|improve this answer


























                          • this should be the top answer! works like a charm!

                            – Ben Usman
                            Aug 10 '17 at 19:45
















                          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





                          share|improve this answer


























                          • this should be the top answer! works like a charm!

                            – Ben Usman
                            Aug 10 '17 at 19:45














                          2












                          2








                          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





                          share|improve this answer

















                          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






                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          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



















                          • 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











                          1














                          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.






                          share|improve this answer




























                            1














                            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.






                            share|improve this answer


























                              1












                              1








                              1







                              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.






                              share|improve this answer













                              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.







                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              answered Mar 14 '14 at 7:31









                              deaksdeaks

                              313




                              313























                                  1














                                  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.






                                  share|improve this answer






























                                    1














                                    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.






                                    share|improve this answer




























                                      1












                                      1








                                      1







                                      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.






                                      share|improve this answer















                                      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.







                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited May 31 '15 at 17:21









                                      serenesat

                                      9181519




                                      9181519










                                      answered Nov 22 '13 at 18:34









                                      JasioJasio

                                      666




                                      666






























                                          draft saved

                                          draft discarded




















































                                          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.




                                          draft saved


                                          draft discarded














                                          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





















































                                          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







                                          Popular posts from this blog

                                          Morgemoulin

                                          Scott Moir

                                          Souastre