Is it possible to redirect the output of a command to one file, but still keep the output echoing on the terminal? - linux

Is it possible to redirect the output of a command to one file, but still keep the output echoing on the terminal?
I want the output to go to a file, but I still want to see it on the terminal (just so I know something is going on)

Yes, tee does what you want:
command | tee output.log

You can use "tee" i.e.
ls -ltr | tee -a mylog.log

Related

Redirect output of command to file and stdout [Only once, not at each command]

I want to have a snapshot of all the commands I executed and output (stdout, stderr) in a file to view later. Often I need to view it for debugging purposes.
This link gives the output for single command:
How to redirect output to a file and stdout
However, I do not want to write it for each command since it is time consuming. Also, this does not log the command I executed.
Example functionality:
bash> start-logging logger.txt
bash> echo "Hi"
Hi
bash> cat 1.txt
Contents of 1.txt
bash> stop-logging
Contents of logger.txt
bash> echo "Hi"
Hi
bash> cat 1.txt
Contents of 1.txt
Ideally, I want the above behavior.
Any other command with some missing functionality (maybe missing command executed from tmp.txt) would also help.
You can use
$ bash | tee log
to start logging and then
$ ^D
(that's Ctrl-D)
to stop logging.
Create a new bash session that will log, then terminate it when it's not needed.
But unfortunately, bash seems to not write its prompt and commands to stderr so you will only catch command output.

How to write stdout to file with colors?

A lot of times (not always) the stdout is displayed in colors. Normally I keep every output log in a different file too. Naturally in the file, the colors are not displayed anymore.
I'd like to know if there's a way (in Linux) to write the output to a file with colors. I'm trying to use tee to write the output of vagrant to a file, this way I can still see the output (when it applies). I want to use it specifically for vagrant (it may change in the future, of course...)
Since many programs will only output color sequences if their stdout is a terminal, a general solution to this problem requires tricking them into believing that the pipe they write to is a terminal. This is possible with the script command from bsdutils:
script -q -c "vagrant up" filename.txt
This will write the output from vagrant up to filename.txt (and the terminal). If echoing is not desirable,
script -q -c "vagrant up" filename > /dev/null
will write it only to the file.
You can save the ANSI sequences that colourise your output to a file:
echo a | grep --color=always . > colour.txt
cat colour.txt
Some programs, though, tend not to use them if their output doesn't go to the terminal (that's why I had to use --color-always with grep).
In Ubuntu, you can install the package bsdutils to output to a text file with ANSI color codes:
script -q -c "ls --color=always" /tmp/t
Install kbtin to generate a clean HTML file:
ls --color=always | ansi2html > /tmp/t.html
Install aha and wkhtmltopdf to generate a nice PDF:
ls --color=always | aha | wkhtmltopdf - /tmp/t.pdf
Use any of the above with tee to display the output also on the console or to save a copy in another file. Example:
ls --color=always | tee /dev/stderr | aha | wkhtmltopdf - /tmp/test.pdf
You can also color your output with echo with different colours and save the coloured output in file. Example
echo -e '\E[37;44m'"Hello World" > my_file
Also You would have to be acquainted with the terminal colour codes
Using tee
< command line > |tee -a 'my_colour_file'
Open your file in cat
cat 'my_colour_file'
Using a named pipe can also work to redirect all output from the pipe with colors to another file
for example
Create a named pipe
mkfifo pipe.fifo
each command line redirect it to the pipe as follows
<command line> > pipe.fifo
In another terminal redirect all messages from the pipe to your file
cat pipe.fifo > 'my_log_file_with_colours'
open your file with cat and see the expected results.
I found out that using the tool called ansi2html.sh
Is the most simple way to export colorful terminal data to html file,
The commands to use it are:
ls --color=always | ansi2html.sh --palette=solarized > ~/Desktop/ls.html
All is needed is to send the output using a pipe and then output the stdout to simple html file
Solution
$ script -q /dev/null -c "your command" > log.txt
$ cat log.txt
Explanation
According to the man page of script, the --quit option only makes sure to be quiet (do not write start and done messages to standard output). Which means that the start and done messages will always be written to the file.
In order to utilize script and discard the output file at the same file, we can simply specify the null device /dev/null to it! Also, redirect the output to our desired destination and the color content will be written to the destination.
I was trying out some of the solutions listed here, and I also realized you could do it with the echo command and the -e flag.
$ echo -e "\e[1;33m This is yellow text \e[0m" > sample.txt
Next, we can view the contents of our sample.txt file.
$ cat sample.txt
Click link to see the output in yellow
Additionally, we can also use tee and pipe it with our echo command:
echo -e "\e[1;33m This is yellow text \e[0m" | tee -a sample.txt
On macOS, script is from the BSD codebase and you can use it like so:
script -q /dev/null mvn dependency:tree mvn-tree.colours.txt
It will run mvn dependency:tree and store the coloured output into mvn-tree.colours.txt
The tee utility supports colours, so you can pipe it to see the command progress:
script -q /dev/null mvn dependency:tree | tee mvn-tree.colours.txt
To get the script manual you can type man script:
SCRIPT(1) BSD General Commands Manual SCRIPT(1)
NAME
script -- make typescript of terminal session
SYNOPSIS
script [-adkpqr] [-F pipe] [-t time] [file [command ...]]
In the RedHat/Rocky/CentOS family, the ansi2html utility does not seem to be available (except for Fedora 32 and up). An equivalent utility is ansifilter from the EPEL repository. Unfortunately, it seems to have been removed from EPEL 8.
script is preinstalled from the util-linux package.
To set up:
yum install ansifilter -y
To use it:
ls --color=always | ansifilter -H > output.html
To generate a pretty PDF (not tested), have ansifilter generate LaTeX output, and then post-process it:
ls --color=always | ansifilter -L | pdflatex >output.pdf
Obviously, combine this with the script utility, or whatever else may be appropriate in your situation.

Redirecting linux cout to a variable and the screen in a script

I am currently trying to make a script file that runs multiple other script files on a server. I would like to display the output of these script to the screen IN ADDITION to passing it into grep so I can do error testing. currently I have written this:
status=$(SOMEPROCESS | grep -i "SOMEPROCESS started completed correctly")
I do further error handling below this using the variable status, so I would like to display SOMEPROCESS's output to the screen for error reference. This is a read only server and I can not save the output to a log file.
You need to use the tee command. It will be slightly fiddly, since tee outputs to a file handle. However you could create a file descriptor using pipe.
Or (simpler) for your use case.
Start the script without grep and pipe it through tee SOMEPROCESS | tee /my/safely/generated/filename. Then use tail -f /my/safely/generated/filename | grep -i "my grep pattern separately.
You can use process substituion together with tee:
SOMEPROCESS | tee >(grep ...)
This will use an anonymous pipe and pass /dev/fd/... as file name to tee (or a named pipe on platforms that don't support /dev/fd/...).
Because SOMEPROCESS is likely to buffer its output when not talking to a terminal, you might see significant lag in screen output.
I'm not sure whether I understood your question exactly.
I think you want to get the output of SOMEPROCESS, test it, print it out when there are errors. If it is, I think the code bellow may help you:
s=$(SOMEPROCESS)
grep -q 'SOMEPROCESS started completed correctly' <<< $s
if [[ $? -ne 0 ]];then
# specified string not found in the output, it means SOMEPROCESS started failed
echo $s
fi
But in this code, it will store the all output in the memory, if the output is big enough, there will be a OOM risk.

Bash standard output display and redirection at the same time

In terminal, sometimes I would like to display the standard output and also save it as a backup. but if I use redirection ( > &> etc), it does not display the output in the terminal anymore.
I think I can do for example ls > localbackup.txt | cat localbackup.txt. But it just doesn't feel right. Is there any shortcut to achieve this?
Thank you!
tee is the command you are looking for:
ls | tee localbackup.txt
In addition to using tee to duplicate the output (and it's worth mentioning that tee is able to append to the file instead of overwriting it, by using tee -a, so that you can run several commands in sequence and retain all of the output), you can also use tail -f to "follow" the output file from a parallel process (e.g. a separate terminal):
command1 >localbackup.txt # create output file
command2 >>localbackup.txt # append to output
and from a separate terminal, at the same time:
tail -f localbackup.txt # this will keep outputting as text is appended to the file

How to redirect output to a file and stdout

In bash, calling foo would display any output from that command on the stdout.
Calling foo > output would redirect any output from that command to the file specified (in this case 'output').
Is there a way to redirect output to a file and have it display on stdout?
The command you want is named tee:
foo | tee output.file
For example, if you only care about stdout:
ls -a | tee output.file
If you want to include stderr, do:
program [arguments...] 2>&1 | tee outfile
2>&1 redirects channel 2 (stderr/standard error) into channel 1 (stdout/standard output), such that both is written as stdout. It is also directed to the given output file as of the tee command.
Furthermore, if you want to append to the log file, use tee -a as:
program [arguments...] 2>&1 | tee -a outfile
$ program [arguments...] 2>&1 | tee outfile
2>&1 dumps the stderr and stdout streams.
tee outfile takes the stream it gets and writes it to the screen and to the file "outfile".
This is probably what most people are looking for. The likely situation is some program or script is working hard for a long time and producing a lot of output. The user wants to check it periodically for progress, but also wants the output written to a file.
The problem (especially when mixing stdout and stderr streams) is that there is reliance on the streams being flushed by the program. If, for example, all the writes to stdout are not flushed, but all the writes to stderr are flushed, then they'll end up out of chronological order in the output file and on the screen.
It's also bad if the program only outputs 1 or 2 lines every few minutes to report progress. In such a case, if the output was not flushed by the program, the user wouldn't even see any output on the screen for hours, because none of it would get pushed through the pipe for hours.
Update: The program unbuffer, part of the expect package, will solve the buffering problem. This will cause stdout and stderr to write to the screen and file immediately and keep them in sync when being combined and redirected to tee. E.g.:
$ unbuffer program [arguments...] 2>&1 | tee outfile
Another way that works for me is,
<command> |& tee <outputFile>
as shown in gnu bash manual
Example:
ls |& tee files.txt
If ‘|&’ is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by the command.
For more information, refer redirection
You can primarily use Zoredache solution, but If you don't want to overwrite the output file you should write tee with -a option as follow :
ls -lR / | tee -a output.file
Something to add ...
The package unbuffer has support issues with some packages under fedora and redhat unix releases.
Setting aside the troubles
Following worked for me
bash myscript.sh 2>&1 | tee output.log
Thank you ScDF & matthew your inputs saved me lot of time..
Using tail -f output should work.
In my case I had the Java process with output logs. The simplest solution to display output logs and redirect them into the file(named logfile here) was:
my_java_process_run_script.sh |& tee logfile
Result was Java process running with output logs displaying and
putting them into the file with name logfile
You can do that for your entire script by using something like that at the beginning of your script :
#!/usr/bin/env bash
test x$1 = x$'\x00' && shift || { set -o pipefail ; ( exec 2>&1 ; $0 $'\x00' "$#" ) | tee mylogfile ; exit $? ; }
# do whaetever you want
This redirect both stderr and stdout outputs to the file called mylogfile and let everything goes to stdout at the same time.
It is used some stupid tricks :
use exec without command to setup redirections,
use tee to duplicates outputs,
restart the script with the wanted redirections,
use a special first parameter (a simple NUL character specified by the $'string' special bash notation) to specify that the script is restarted (no equivalent parameter may be used by your original work),
try to preserve the original exit status when restarting the script using the pipefail option.
Ugly but useful for me in certain situations.
Bonus answer since this use-case brought me here:
In the case where you need to do this as some other user
echo "some output" | sudo -u some_user tee /some/path/some_file
Note that the echo will happen as you and the file write will happen as "some_user" what will NOT work is if you were to run the echo as "some_user" and redirect the output with >> "some_file" because the file redirect will happen as you.
Hint: tee also supports append with the -a flag, if you need to replace a line in a file as another user you could execute sed as the desired user.
< command > |& tee filename # this will create a file "filename" with command status as a content, If a file already exists it will remove existed content and writes the command status.
< command > | tee >> filename # this will append status to the file but it doesn't print the command status on standard_output (screen).
I want to print something by using "echo" on screen and append that echoed data to a file
echo "hi there, Have to print this on screen and append to a file"
tee is perfect for this, but this will also do the job
ls -lr / > output | cat output

Resources