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
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
add a comment |
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
add a comment |
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
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
bash
asked 2 hours ago
JonahJonah
4291512
4291512
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
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 ORSThis appends each line of input to the variable
x.In awk,
ORSis the output record separator. By default, it is a newline character.ENDprintf "%s", $0 ORS xAfter 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.
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 wayteedoes, 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
add a comment |
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
add a comment |
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
- Provide the full contents of the document to
tail. - Provide it again to
cat. - 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
1h;d;, store the first line in the hold space, as-is, and skip to the next line.H, append each other line to the hold space with an embedded newline.$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.
Thesedone doesn't work if the input has only one line (maybesed '1h;1!H;$!d;G'). Also note that severalsedimplementations have a low limit on the size of their pattern and hold space.
– Stéphane Chazelas
23 mins ago
add a comment |
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.
add a comment |
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%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
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 ORSThis appends each line of input to the variable
x.In awk,
ORSis the output record separator. By default, it is a newline character.ENDprintf "%s", $0 ORS xAfter 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.
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 wayteedoes, 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
add a comment |
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 ORSThis appends each line of input to the variable
x.In awk,
ORSis the output record separator. By default, it is a newline character.ENDprintf "%s", $0 ORS xAfter 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.
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 wayteedoes, 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
add a comment |
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 ORSThis appends each line of input to the variable
x.In awk,
ORSis the output record separator. By default, it is a newline character.ENDprintf "%s", $0 ORS xAfter 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.
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 ORSThis appends each line of input to the variable
x.In awk,
ORSis the output record separator. By default, it is a newline character.ENDprintf "%s", $0 ORS xAfter 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.
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 wayteedoes, 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
add a comment |
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 wayteedoes, 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
add a comment |
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
add a comment |
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
add a comment |
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
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
answered 27 mins ago
Stéphane ChazelasStéphane Chazelas
312k57589946
312k57589946
add a comment |
add a comment |
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
- Provide the full contents of the document to
tail. - Provide it again to
cat. - 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
1h;d;, store the first line in the hold space, as-is, and skip to the next line.H, append each other line to the hold space with an embedded newline.$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.
Thesedone doesn't work if the input has only one line (maybesed '1h;1!H;$!d;G'). Also note that severalsedimplementations have a low limit on the size of their pattern and hold space.
– Stéphane Chazelas
23 mins ago
add a comment |
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
- Provide the full contents of the document to
tail. - Provide it again to
cat. - 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
1h;d;, store the first line in the hold space, as-is, and skip to the next line.H, append each other line to the hold space with an embedded newline.$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.
Thesedone doesn't work if the input has only one line (maybesed '1h;1!H;$!d;G'). Also note that severalsedimplementations have a low limit on the size of their pattern and hold space.
– Stéphane Chazelas
23 mins ago
add a comment |
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
- Provide the full contents of the document to
tail. - Provide it again to
cat. - 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
1h;d;, store the first line in the hold space, as-is, and skip to the next line.H, append each other line to the hold space with an embedded newline.$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.
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
- Provide the full contents of the document to
tail. - Provide it again to
cat. - 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
1h;d;, store the first line in the hold space, as-is, and skip to the next line.H, append each other line to the hold space with an embedded newline.$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.
edited 4 mins ago
answered 1 hour ago
Kusalananda♦Kusalananda
138k17258428
138k17258428
Thesedone doesn't work if the input has only one line (maybesed '1h;1!H;$!d;G'). Also note that severalsedimplementations have a low limit on the size of their pattern and hold space.
– Stéphane Chazelas
23 mins ago
add a comment |
Thesedone doesn't work if the input has only one line (maybesed '1h;1!H;$!d;G'). Also note that severalsedimplementations 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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered 1 hour ago
ctrl-alt-delorctrl-alt-delor
12.2k42561
12.2k42561
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509621%2fprepend-last-line-of-stdin-to-entire-stdin%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown