Aside from using a temporary file to help, is there a way/program could buffer input from stdin but does not output the contents until getting the EOF. I don’t want to use a shell variable either(e.g. buffer=$(cat)).
This program should behave as below(assume the program name is buffered-cat):
$ buffered-cat line 1 line 2 line 3 ^D # Ctr-D here(End of Line)
Now that the program received ^D, the buffered-cat outputs the contents
line 1 line 2 line 3
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You can do this with sponge from moreutils. sponge will “soak up standard input and write to a file”. With no arguments, that file is standard output. Input given to this command is stored in memory until EOF, and then written out all at once.
For writing to a normal file, you can just give the filename:
cmd | sponge filename
The main purpose of sponge is to allow reading and writing from the same file within a pipeline, but it does what you want as well.
Method 2
A poor man’s sponge using awk:
awk '{a[NR] = $0} END {for (i = 1; i <= NR; i++) print a[i]}'
If you have tac, you can misuse it too:
... | tac | tac
Method 3
As long as your input is ASCII text (contains no NUL 0x0 bytes until the end), then sed -z does what you want:
$ sed -z '' Line 1 Line 2 Line 3 ^D Line 1 Line 2 Line 3 $
The -z causes sed to treat the NUL byte as a line delimiter instead of the usual newline. So as long as your input is regular text with no NUL bytes, then sed will continue reading the whole input into its pattern buffer until EOF is reached. sed then does no processing on the buffer and outputs it.
If NUL bytes are present in your input, then you can do this instead:
sed ':l;N;bl'
Method 4
This sed solution is a little longer than DigitalTrauma’s, but also works with NUL bytes.
sed -n 'H;${x;s/^n//;p}'
Method 5
Same idea as muru’s awk example, except in Python. Use CtrlD to stop reading in lines
$ python -c 'import sys;print("".join(sys.stdin.readlines()))'
line1
line2
line3 # press Enter and Ctrl+D at this point
line1
line2
line3
Method 6
A trivial one-line sponge-replacement in Perl:
perl -e 'print <>'
(from @dave_thompson_085, in a comment)
Perl should be able to deal with NUL bytes too.
Method 7
You can use the following:
#!/bin/bash
stdin="$(cat /dev/stdin ; echo . )"
# To preserve newlines at the end we add a "." above and remove it below. See: https://unix.stackexchange.com/a/383411/62628
stdin=${stdin%.}
echo -n "$stdin"
$ buffered-cat line 1 line 2 line 3 ^D # Ctr-D here(End of Line) line 1 line 2 line 3 $ buffered-cat
But since the command is called bufferred-cat you might want to be able to pass all options of cat. In this case:
#!/bin/bash
stdin="$(cat /dev/stdin ; echo . )"
stdin=${stdin%.}
cat "[email protected]" <(echo -n "$stdin")
$ buffered-cat -n
line 1
line 2
line 3
^D # Ctr-D here(End of Line)
1 line 1
2 line 2
3 line 3
$ buffered-cat
Method 8
The behavior you are asking for, isn’t it the default behavior of simple cat?
<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e68190a68283848f8788">[email protected]</a>:$ cat << EOF #or cat <<EOF >file or cat <<EOF >/dev/stdout > Line 1 > Line 2 > Line 3 > EOF Line 1 Line 2 Line 3 <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a9cedfe9cdcccbc0c8c7">[email protected]</a>:$
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0