Prepend last line of stdin to entire stdin The Next CEO of Stack OverflowHow can I get awk to print both matches and non-matches?Understanding Bash's Read-a-File Command SubstitutionHow to determine interface RUNNING state without ifconfig?Read a password securely from within process substitution?bash script that incorporates content from a file as part of a commandEcho new line and string beginning tbash: IFS not honoured by read commandPrepend string before each line of stdinHow to I tail the STDOUT of a cat commandLog redirection happens to rotated log instead of new log getting created

Why isn't the Mueller report being released completely and unredacted?

Prepend last line of stdin to entire stdin

Why do airplanes bank sharply to the right after air-to-air refueling?

Decomposition of product of two Plucker coordinates

What was the first Unix version to run on a microcomputer?

What happened in Rome, when the western empire "fell"?

is it ok to reduce charging current for li ion 18650 battery?

Need help understanding a power circuit (caps and diodes)

Circle x^2 + y^2 = n! doesn't hit any lattice points for any n except for 0, 1, 2 and 6 or does it?

Why doesn't UK go for the same deal Japan has with EU to resolve Brexit?

What did we know about the Kessel run before the prologues?

Can MTA send mail via a relay without being told so?

Measuring resistivity of dielectric liquid

Does increasing your ability score affect your main stat?

How many extra stops do monopods offer for tele photographs?

Rotate a column

Is French Guiana a (hard) EU border?

The exact meaning of 'Mom made me a sandwich'

Make solar eclipses exceedingly rare, but still have new moons

If Nick Fury and Coulson already knew about aliens (Kree and Skrull) why did they wait until Thor's appearance to start making weapons?

Why do remote US companies require working in the US?

Some questions about different axiomatic systems for neighbourhoods

Domestic-to-international connection at Orlando (MCO)

Example of a Mathematician/Physicist whose Other Publications during their PhD eclipsed their PhD Thesis



Prepend last line of stdin to entire stdin



The Next CEO of Stack OverflowHow can I get awk to print both matches and non-matches?Understanding Bash's Read-a-File Command SubstitutionHow to determine interface RUNNING state without ifconfig?Read a password securely from within process substitution?bash script that incorporates content from a file as part of a commandEcho new line and string beginning tbash: IFS not honoured by read commandPrepend string before each line of stdinHow to I tail the STDOUT of a cat commandLog redirection happens to rotated log instead of new log getting created










2















Consider this script:



tmpfile=$(mktemp)

cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS

cat <(tail -1 "$tmpfile") "$tmpfile"


This works and outputs:



line 3
line 1
line 2
line 3


Let's say that our input source, rather than being an actual file, was instead stdin:



cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS


How do we modify the command:



cat <(tail -1 "$tmpfile") "$tmpfile"


So that it still produces the same output, in this different context?










share|improve this question


























    2















    Consider this script:



    tmpfile=$(mktemp)

    cat <<EOS > "$tmpfile"
    line 1
    line 2
    line 3
    EOS

    cat <(tail -1 "$tmpfile") "$tmpfile"


    This works and outputs:



    line 3
    line 1
    line 2
    line 3


    Let's say that our input source, rather than being an actual file, was instead stdin:



    cat <<EOS | # what goes here now?
    line 1
    line 2
    line 3
    EOS


    How do we modify the command:



    cat <(tail -1 "$tmpfile") "$tmpfile"


    So that it still produces the same output, in this different context?










    share|improve this question
























      2












      2








      2








      Consider this script:



      tmpfile=$(mktemp)

      cat <<EOS > "$tmpfile"
      line 1
      line 2
      line 3
      EOS

      cat <(tail -1 "$tmpfile") "$tmpfile"


      This works and outputs:



      line 3
      line 1
      line 2
      line 3


      Let's say that our input source, rather than being an actual file, was instead stdin:



      cat <<EOS | # what goes here now?
      line 1
      line 2
      line 3
      EOS


      How do we modify the command:



      cat <(tail -1 "$tmpfile") "$tmpfile"


      So that it still produces the same output, in this different context?










      share|improve this question














      Consider this script:



      tmpfile=$(mktemp)

      cat <<EOS > "$tmpfile"
      line 1
      line 2
      line 3
      EOS

      cat <(tail -1 "$tmpfile") "$tmpfile"


      This works and outputs:



      line 3
      line 1
      line 2
      line 3


      Let's say that our input source, rather than being an actual file, was instead stdin:



      cat <<EOS | # what goes here now?
      line 1
      line 2
      line 3
      EOS


      How do we modify the command:



      cat <(tail -1 "$tmpfile") "$tmpfile"


      So that it still produces the same output, in this different context?







      bash






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 2 hours ago









      JonahJonah

      4291512




      4291512




















          4 Answers
          4






          active

          oldest

          votes


















          3














          Try:



          awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'


          Example



          Define a variable with our input:



          $ input="line 1
          > line 2
          > line 3"


          Run our command:



          $ echo "$input" | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
          line 3
          line 1
          line 2
          line 3


          Alternatively, of course, we could use a here-doc:



          $ cat <<EOS | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
          line 1
          line 2
          line 3
          EOS
          line 3
          line 1
          line 2
          line 3


          How it works




          • x=x $0 ORS



            This appends each line of input to the variable x.



            In awk, ORS is the output record separator. By default, it is a newline character.




          • ENDprintf "%s", $0 ORS x



            After the we have read in the whole file, this prints the last line, $0, followed by the contents of the whole file, x.



          Since this reads the whole input into memory, it would not be appropriate for large (e.g. gigabyte) inputs.






          share|improve this answer

























          • Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

            – Jonah
            1 hour ago


















          3














          If stdin points to a seekable file (like in the case of bash's (but not all other shell's) here documents which are implemented with temp files), you can get the tail and then seek back before reading the full contents:



          seek operators are available in the zsh or ksh93 shells, or scripting languages like tcl/perl/python, but not in bash. But you can always call those more advanced interpreters from bash if you have to use bash.



          ksh93 -c 'tail -n1; cat <#((0))' <<...


          Or



          zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...


          Now, that won't work when stdin points to a non-seekable files like a pipe or socket. Then, the only option is to read and store (in memory or in a temporary file...) the whole input.



          Some solutions for storing in memory have already been given.



          With a tempfile, with zsh, you could do it with:



          seq 10 | zsh -c ' cat =(sed $w/dev/fd/3); 3>&1'


          If on Linux, with bash or zsh or any shell that uses temp files for here-documents, you could actually use the temp file created by a here-document to store the output:



          seq 10 | cat > /dev/fd/3; tail -n1 /dev/fd/3; cat <&3; 3<<EOF
          EOF





          share|improve this answer






























            2














            cat <<EOS | sed -ne '1h;d;' -e 'H;$G;p;'
            line 1
            line 2
            line 3
            EOS


            The issue with translating this to something that uses tail is that tail needs to read the whole file to find the end of it. To use that in your pipeline, you need to



            1. Provide the full contents of the document to tail.

            2. Provide it again to cat.

            3. In that order.

            The tricky bit is not to duplicate the document's content (tee does that) but to get the output of tail to happen before the rest of the document is outputted, without using an intermediate temporary file.



            Using sed (or awk, as John1024 does) gets rid of the double parsing of the data and the ordering issue by storing the data in memory.



            The sed solution that I propose is to




            1. 1h;d;, store the first line in the hold space, as-is, and skip to the next line.


            2. H, append each other line to the hold space with an embedded newline.


            3. $G;p;, append the hold space to the last line with an embedded newline and print the resulting data.

            This is quite a literal translation of John1024's solution into sed, with the caveat that the POSIX standard only guarantees that the hold space is at lest 8192 bytes (8 KiB; but it recommends that this buffer is dynamically allocated and expanded as needed).




            If you allow yourself to use a named pipe:



            mkfifo mypipe
            cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
            line 1
            line 2
            line 3
            EOS
            rm -f mypipe


            This uses tee to send the data down mypipe and at the same time to cat. The cat utility will first read the output from tail (which reads from mypipe, which tee is writing to), and then append the copy of the document coming directly from tee.



            There's a serious flaw in this though, in that if the document is too large (larger than the pipe's buffer size), tee's writing to mypipe and cat would block while waiting for the (unnamed) pipe to empty. It would not be emptied until cat read from it. cat would not read from it until tail had finished. And tail would not finish until tee had finished. This is a classic deadlock situation.



            The variation



            tee >( tail -n 1 >mypipe ) | cat mypipe -


            has the same issue.






            share|improve this answer

























            • The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

              – Stéphane Chazelas
              23 mins ago



















            0














            If you don't care about the order. Then this will work cat lines | tee >(tail -1).
            As others have said. You need to read file twice, or buffer the whole file, to do it in the order you asked for.






            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%2f509621%2fprepend-last-line-of-stdin-to-entire-stdin%23new-answer', 'question_page');

              );

              Post as a guest















              Required, but never shown

























              4 Answers
              4






              active

              oldest

              votes








              4 Answers
              4






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              3














              Try:



              awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'


              Example



              Define a variable with our input:



              $ input="line 1
              > line 2
              > line 3"


              Run our command:



              $ echo "$input" | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 3
              line 1
              line 2
              line 3


              Alternatively, of course, we could use a here-doc:



              $ cat <<EOS | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 1
              line 2
              line 3
              EOS
              line 3
              line 1
              line 2
              line 3


              How it works




              • x=x $0 ORS



                This appends each line of input to the variable x.



                In awk, ORS is the output record separator. By default, it is a newline character.




              • ENDprintf "%s", $0 ORS x



                After the we have read in the whole file, this prints the last line, $0, followed by the contents of the whole file, x.



              Since this reads the whole input into memory, it would not be appropriate for large (e.g. gigabyte) inputs.






              share|improve this answer

























              • Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

                – Jonah
                1 hour ago















              3














              Try:



              awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'


              Example



              Define a variable with our input:



              $ input="line 1
              > line 2
              > line 3"


              Run our command:



              $ echo "$input" | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 3
              line 1
              line 2
              line 3


              Alternatively, of course, we could use a here-doc:



              $ cat <<EOS | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 1
              line 2
              line 3
              EOS
              line 3
              line 1
              line 2
              line 3


              How it works




              • x=x $0 ORS



                This appends each line of input to the variable x.



                In awk, ORS is the output record separator. By default, it is a newline character.




              • ENDprintf "%s", $0 ORS x



                After the we have read in the whole file, this prints the last line, $0, followed by the contents of the whole file, x.



              Since this reads the whole input into memory, it would not be appropriate for large (e.g. gigabyte) inputs.






              share|improve this answer

























              • Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

                – Jonah
                1 hour ago













              3












              3








              3







              Try:



              awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'


              Example



              Define a variable with our input:



              $ input="line 1
              > line 2
              > line 3"


              Run our command:



              $ echo "$input" | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 3
              line 1
              line 2
              line 3


              Alternatively, of course, we could use a here-doc:



              $ cat <<EOS | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 1
              line 2
              line 3
              EOS
              line 3
              line 1
              line 2
              line 3


              How it works




              • x=x $0 ORS



                This appends each line of input to the variable x.



                In awk, ORS is the output record separator. By default, it is a newline character.




              • ENDprintf "%s", $0 ORS x



                After the we have read in the whole file, this prints the last line, $0, followed by the contents of the whole file, x.



              Since this reads the whole input into memory, it would not be appropriate for large (e.g. gigabyte) inputs.






              share|improve this answer















              Try:



              awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'


              Example



              Define a variable with our input:



              $ input="line 1
              > line 2
              > line 3"


              Run our command:



              $ echo "$input" | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 3
              line 1
              line 2
              line 3


              Alternatively, of course, we could use a here-doc:



              $ cat <<EOS | awk 'x=x $0 ORS; ENDprintf "%s", $0 ORS x'
              line 1
              line 2
              line 3
              EOS
              line 3
              line 1
              line 2
              line 3


              How it works




              • x=x $0 ORS



                This appends each line of input to the variable x.



                In awk, ORS is the output record separator. By default, it is a newline character.




              • ENDprintf "%s", $0 ORS x



                After the we have read in the whole file, this prints the last line, $0, followed by the contents of the whole file, x.



              Since this reads the whole input into memory, it would not be appropriate for large (e.g. gigabyte) inputs.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 1 hour ago









              Stéphane Chazelas

              312k57589946




              312k57589946










              answered 1 hour ago









              John1024John1024

              48.1k5113127




              48.1k5113127












              • Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

                – Jonah
                1 hour ago

















              • Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

                – Jonah
                1 hour ago
















              Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

              – Jonah
              1 hour ago





              Thanks John. So is it not possible to do this in a way analogous to my named file example in the OP? I was imagining the stdin being duplicated somehow... sort of the way tee does, but of a stdin and a file, we'd be piping the same stdin into two different process substitutions. or anything that would be roughly equivalent to that?

              – Jonah
              1 hour ago













              3














              If stdin points to a seekable file (like in the case of bash's (but not all other shell's) here documents which are implemented with temp files), you can get the tail and then seek back before reading the full contents:



              seek operators are available in the zsh or ksh93 shells, or scripting languages like tcl/perl/python, but not in bash. But you can always call those more advanced interpreters from bash if you have to use bash.



              ksh93 -c 'tail -n1; cat <#((0))' <<...


              Or



              zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...


              Now, that won't work when stdin points to a non-seekable files like a pipe or socket. Then, the only option is to read and store (in memory or in a temporary file...) the whole input.



              Some solutions for storing in memory have already been given.



              With a tempfile, with zsh, you could do it with:



              seq 10 | zsh -c ' cat =(sed $w/dev/fd/3); 3>&1'


              If on Linux, with bash or zsh or any shell that uses temp files for here-documents, you could actually use the temp file created by a here-document to store the output:



              seq 10 | cat > /dev/fd/3; tail -n1 /dev/fd/3; cat <&3; 3<<EOF
              EOF





              share|improve this answer



























                3














                If stdin points to a seekable file (like in the case of bash's (but not all other shell's) here documents which are implemented with temp files), you can get the tail and then seek back before reading the full contents:



                seek operators are available in the zsh or ksh93 shells, or scripting languages like tcl/perl/python, but not in bash. But you can always call those more advanced interpreters from bash if you have to use bash.



                ksh93 -c 'tail -n1; cat <#((0))' <<...


                Or



                zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...


                Now, that won't work when stdin points to a non-seekable files like a pipe or socket. Then, the only option is to read and store (in memory or in a temporary file...) the whole input.



                Some solutions for storing in memory have already been given.



                With a tempfile, with zsh, you could do it with:



                seq 10 | zsh -c ' cat =(sed $w/dev/fd/3); 3>&1'


                If on Linux, with bash or zsh or any shell that uses temp files for here-documents, you could actually use the temp file created by a here-document to store the output:



                seq 10 | cat > /dev/fd/3; tail -n1 /dev/fd/3; cat <&3; 3<<EOF
                EOF





                share|improve this answer

























                  3












                  3








                  3







                  If stdin points to a seekable file (like in the case of bash's (but not all other shell's) here documents which are implemented with temp files), you can get the tail and then seek back before reading the full contents:



                  seek operators are available in the zsh or ksh93 shells, or scripting languages like tcl/perl/python, but not in bash. But you can always call those more advanced interpreters from bash if you have to use bash.



                  ksh93 -c 'tail -n1; cat <#((0))' <<...


                  Or



                  zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...


                  Now, that won't work when stdin points to a non-seekable files like a pipe or socket. Then, the only option is to read and store (in memory or in a temporary file...) the whole input.



                  Some solutions for storing in memory have already been given.



                  With a tempfile, with zsh, you could do it with:



                  seq 10 | zsh -c ' cat =(sed $w/dev/fd/3); 3>&1'


                  If on Linux, with bash or zsh or any shell that uses temp files for here-documents, you could actually use the temp file created by a here-document to store the output:



                  seq 10 | cat > /dev/fd/3; tail -n1 /dev/fd/3; cat <&3; 3<<EOF
                  EOF





                  share|improve this answer













                  If stdin points to a seekable file (like in the case of bash's (but not all other shell's) here documents which are implemented with temp files), you can get the tail and then seek back before reading the full contents:



                  seek operators are available in the zsh or ksh93 shells, or scripting languages like tcl/perl/python, but not in bash. But you can always call those more advanced interpreters from bash if you have to use bash.



                  ksh93 -c 'tail -n1; cat <#((0))' <<...


                  Or



                  zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...


                  Now, that won't work when stdin points to a non-seekable files like a pipe or socket. Then, the only option is to read and store (in memory or in a temporary file...) the whole input.



                  Some solutions for storing in memory have already been given.



                  With a tempfile, with zsh, you could do it with:



                  seq 10 | zsh -c ' cat =(sed $w/dev/fd/3); 3>&1'


                  If on Linux, with bash or zsh or any shell that uses temp files for here-documents, you could actually use the temp file created by a here-document to store the output:



                  seq 10 | cat > /dev/fd/3; tail -n1 /dev/fd/3; cat <&3; 3<<EOF
                  EOF






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 27 mins ago









                  Stéphane ChazelasStéphane Chazelas

                  312k57589946




                  312k57589946





















                      2














                      cat <<EOS | sed -ne '1h;d;' -e 'H;$G;p;'
                      line 1
                      line 2
                      line 3
                      EOS


                      The issue with translating this to something that uses tail is that tail needs to read the whole file to find the end of it. To use that in your pipeline, you need to



                      1. Provide the full contents of the document to tail.

                      2. Provide it again to cat.

                      3. In that order.

                      The tricky bit is not to duplicate the document's content (tee does that) but to get the output of tail to happen before the rest of the document is outputted, without using an intermediate temporary file.



                      Using sed (or awk, as John1024 does) gets rid of the double parsing of the data and the ordering issue by storing the data in memory.



                      The sed solution that I propose is to




                      1. 1h;d;, store the first line in the hold space, as-is, and skip to the next line.


                      2. H, append each other line to the hold space with an embedded newline.


                      3. $G;p;, append the hold space to the last line with an embedded newline and print the resulting data.

                      This is quite a literal translation of John1024's solution into sed, with the caveat that the POSIX standard only guarantees that the hold space is at lest 8192 bytes (8 KiB; but it recommends that this buffer is dynamically allocated and expanded as needed).




                      If you allow yourself to use a named pipe:



                      mkfifo mypipe
                      cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
                      line 1
                      line 2
                      line 3
                      EOS
                      rm -f mypipe


                      This uses tee to send the data down mypipe and at the same time to cat. The cat utility will first read the output from tail (which reads from mypipe, which tee is writing to), and then append the copy of the document coming directly from tee.



                      There's a serious flaw in this though, in that if the document is too large (larger than the pipe's buffer size), tee's writing to mypipe and cat would block while waiting for the (unnamed) pipe to empty. It would not be emptied until cat read from it. cat would not read from it until tail had finished. And tail would not finish until tee had finished. This is a classic deadlock situation.



                      The variation



                      tee >( tail -n 1 >mypipe ) | cat mypipe -


                      has the same issue.






                      share|improve this answer

























                      • The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                        – Stéphane Chazelas
                        23 mins ago
















                      2














                      cat <<EOS | sed -ne '1h;d;' -e 'H;$G;p;'
                      line 1
                      line 2
                      line 3
                      EOS


                      The issue with translating this to something that uses tail is that tail needs to read the whole file to find the end of it. To use that in your pipeline, you need to



                      1. Provide the full contents of the document to tail.

                      2. Provide it again to cat.

                      3. In that order.

                      The tricky bit is not to duplicate the document's content (tee does that) but to get the output of tail to happen before the rest of the document is outputted, without using an intermediate temporary file.



                      Using sed (or awk, as John1024 does) gets rid of the double parsing of the data and the ordering issue by storing the data in memory.



                      The sed solution that I propose is to




                      1. 1h;d;, store the first line in the hold space, as-is, and skip to the next line.


                      2. H, append each other line to the hold space with an embedded newline.


                      3. $G;p;, append the hold space to the last line with an embedded newline and print the resulting data.

                      This is quite a literal translation of John1024's solution into sed, with the caveat that the POSIX standard only guarantees that the hold space is at lest 8192 bytes (8 KiB; but it recommends that this buffer is dynamically allocated and expanded as needed).




                      If you allow yourself to use a named pipe:



                      mkfifo mypipe
                      cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
                      line 1
                      line 2
                      line 3
                      EOS
                      rm -f mypipe


                      This uses tee to send the data down mypipe and at the same time to cat. The cat utility will first read the output from tail (which reads from mypipe, which tee is writing to), and then append the copy of the document coming directly from tee.



                      There's a serious flaw in this though, in that if the document is too large (larger than the pipe's buffer size), tee's writing to mypipe and cat would block while waiting for the (unnamed) pipe to empty. It would not be emptied until cat read from it. cat would not read from it until tail had finished. And tail would not finish until tee had finished. This is a classic deadlock situation.



                      The variation



                      tee >( tail -n 1 >mypipe ) | cat mypipe -


                      has the same issue.






                      share|improve this answer

























                      • The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                        – Stéphane Chazelas
                        23 mins ago














                      2












                      2








                      2







                      cat <<EOS | sed -ne '1h;d;' -e 'H;$G;p;'
                      line 1
                      line 2
                      line 3
                      EOS


                      The issue with translating this to something that uses tail is that tail needs to read the whole file to find the end of it. To use that in your pipeline, you need to



                      1. Provide the full contents of the document to tail.

                      2. Provide it again to cat.

                      3. In that order.

                      The tricky bit is not to duplicate the document's content (tee does that) but to get the output of tail to happen before the rest of the document is outputted, without using an intermediate temporary file.



                      Using sed (or awk, as John1024 does) gets rid of the double parsing of the data and the ordering issue by storing the data in memory.



                      The sed solution that I propose is to




                      1. 1h;d;, store the first line in the hold space, as-is, and skip to the next line.


                      2. H, append each other line to the hold space with an embedded newline.


                      3. $G;p;, append the hold space to the last line with an embedded newline and print the resulting data.

                      This is quite a literal translation of John1024's solution into sed, with the caveat that the POSIX standard only guarantees that the hold space is at lest 8192 bytes (8 KiB; but it recommends that this buffer is dynamically allocated and expanded as needed).




                      If you allow yourself to use a named pipe:



                      mkfifo mypipe
                      cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
                      line 1
                      line 2
                      line 3
                      EOS
                      rm -f mypipe


                      This uses tee to send the data down mypipe and at the same time to cat. The cat utility will first read the output from tail (which reads from mypipe, which tee is writing to), and then append the copy of the document coming directly from tee.



                      There's a serious flaw in this though, in that if the document is too large (larger than the pipe's buffer size), tee's writing to mypipe and cat would block while waiting for the (unnamed) pipe to empty. It would not be emptied until cat read from it. cat would not read from it until tail had finished. And tail would not finish until tee had finished. This is a classic deadlock situation.



                      The variation



                      tee >( tail -n 1 >mypipe ) | cat mypipe -


                      has the same issue.






                      share|improve this answer















                      cat <<EOS | sed -ne '1h;d;' -e 'H;$G;p;'
                      line 1
                      line 2
                      line 3
                      EOS


                      The issue with translating this to something that uses tail is that tail needs to read the whole file to find the end of it. To use that in your pipeline, you need to



                      1. Provide the full contents of the document to tail.

                      2. Provide it again to cat.

                      3. In that order.

                      The tricky bit is not to duplicate the document's content (tee does that) but to get the output of tail to happen before the rest of the document is outputted, without using an intermediate temporary file.



                      Using sed (or awk, as John1024 does) gets rid of the double parsing of the data and the ordering issue by storing the data in memory.



                      The sed solution that I propose is to




                      1. 1h;d;, store the first line in the hold space, as-is, and skip to the next line.


                      2. H, append each other line to the hold space with an embedded newline.


                      3. $G;p;, append the hold space to the last line with an embedded newline and print the resulting data.

                      This is quite a literal translation of John1024's solution into sed, with the caveat that the POSIX standard only guarantees that the hold space is at lest 8192 bytes (8 KiB; but it recommends that this buffer is dynamically allocated and expanded as needed).




                      If you allow yourself to use a named pipe:



                      mkfifo mypipe
                      cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
                      line 1
                      line 2
                      line 3
                      EOS
                      rm -f mypipe


                      This uses tee to send the data down mypipe and at the same time to cat. The cat utility will first read the output from tail (which reads from mypipe, which tee is writing to), and then append the copy of the document coming directly from tee.



                      There's a serious flaw in this though, in that if the document is too large (larger than the pipe's buffer size), tee's writing to mypipe and cat would block while waiting for the (unnamed) pipe to empty. It would not be emptied until cat read from it. cat would not read from it until tail had finished. And tail would not finish until tee had finished. This is a classic deadlock situation.



                      The variation



                      tee >( tail -n 1 >mypipe ) | cat mypipe -


                      has the same issue.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited 4 mins ago

























                      answered 1 hour ago









                      KusalanandaKusalananda

                      138k17258428




                      138k17258428












                      • The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                        – Stéphane Chazelas
                        23 mins ago


















                      • The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                        – Stéphane Chazelas
                        23 mins ago

















                      The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                      – Stéphane Chazelas
                      23 mins ago






                      The sed one doesn't work if the input has only one line (maybe sed '1h;1!H;$!d;G'). Also note that several sed implementations have a low limit on the size of their pattern and hold space.

                      – Stéphane Chazelas
                      23 mins ago












                      0














                      If you don't care about the order. Then this will work cat lines | tee >(tail -1).
                      As others have said. You need to read file twice, or buffer the whole file, to do it in the order you asked for.






                      share|improve this answer



























                        0














                        If you don't care about the order. Then this will work cat lines | tee >(tail -1).
                        As others have said. You need to read file twice, or buffer the whole file, to do it in the order you asked for.






                        share|improve this answer

























                          0












                          0








                          0







                          If you don't care about the order. Then this will work cat lines | tee >(tail -1).
                          As others have said. You need to read file twice, or buffer the whole file, to do it in the order you asked for.






                          share|improve this answer













                          If you don't care about the order. Then this will work cat lines | tee >(tail -1).
                          As others have said. You need to read file twice, or buffer the whole file, to do it in the order you asked for.







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered 1 hour ago









                          ctrl-alt-delorctrl-alt-delor

                          12.2k42561




                          12.2k42561



























                              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%2f509621%2fprepend-last-line-of-stdin-to-entire-stdin%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

                              acmart: Multiple authors: all with same affiliation, one author an additional affiliationHow to Write Names of Multiple Authors with Shared Affiliation in ACM 2017 Template?Multiple authors with different primary affiliation, but same additional affiliationSame affiliation for all authors without extra packagesIOS-Book-Article.cls: one author with multiple affiliationacmart: Shared Author AffiliationMultiple authors with different primary affiliation, but same additional affiliationAuthor affiliation with only 1 authorAdding Multiple Authors with Different Affiliation in LaTeX ArticleLaTeX: Multiple authors stays on same lineHow to Label Multiple Authors with Same DescriptionHow to make two authors use the same affiliationTwo authors with same affiliation on finished front page

                              How to write “ä” and other umlauts and accented letters in bibliography?Accents in BibTeXSorting references with special characters alphabeticallyUse ae ligature in bibliographyEastern European nameInverted circumflex in BibTexBibTex, non-ascii initials and nameptr fproblems with accent in LatexHow to add a Ø to my bibliography from Jabref?References without accentsTroubles when trying to cite St“omer-Verlet in ”title" field of a bib entryComprehensive list of accented charactersHow to type the letter “i” with two dots (diaeresis) in math mode?Problem with glossary text and accented lettersSpecial character in bibliographyAccented letters, Unicode and LaTeX accentsHow to stop natbib from modifying bibliography styleCitation of a paper with non-standard characters by BibtexWrite accented characters to file using writeHow to group the bibliography alphabetically, if some surnames start with “accented” characters?How can I automatically capitalize significant words in my bibliography?

                              Problem using RevTeX4-1 with “! Undefined control sequence. @bibitemShut”