log ALL output of console - linux

I am trying to log something in the console, but it doesn't work. The output is:
Resolving...
+ /keyboard
+ /keyboard/windows.keylayout
+ /keyboard/hardware.txt
Addition count 3 src: 60.42KB
61868 / 61868 [====================================================] 100.00% 0s
I want that into a .log file.
But it only takes "Resolving...", when I execute "script >> script.log" as the first line, I guess that's because of the "+", how can I fix that, that it will log ALL of the output into that file?
cd /home/andre/test/ && /usr/local/bin/drive_armv7 pull -no-prompt keyboard 2>&1 >> error.log
Thanks.

Order of assignment?
cd /home/andre/test/ && /usr/local/bin/drive_armv7 pull -no-prompt keyboard 2>&1 >> error.log
tells stderr to go where stdout is going, which is the terminal. THEN you tell stdout to go into the log. Switch them -
cd /home/andre/test/ &&
/usr/local/bin/drive_armv7 pull -no-prompt keyboard >> error.log 2>&1
That tells stdout to go into the log, THEN tells stderr to go where stdout is going...which is now the log.

It could be that you are running a default program called script
user#apollo:~$ script test
Script started, file is test
user#apollo:~$ which script
/usr/bin/script
If "script" is a local executable called "script.sh", you should run it as following
./script.sh
Trying to reproduce your issue gives no unexpected output
cat > script.sh <<EOF
echo "Resolving..."
echo "+ /keyboard"
echo "+ /keyboard/windows.keylayout"
echo "+ /keyboard/hardware.txt"
echo "Addition count 3 src: 60.42KB"
echo "61868 / 61868 [====================================================] 100.00% 0s"
EOF
chmod +x script.sh
./script.sh >> script.log
cat script.log
The unexpected behaviour depends on what your script is trying to do

Related

How to run script multiple times and after every execution of command to wait until the device is ready to execute again?

I have this bash script:
#!/bin/bash
rm /etc/stress.txt
cat /dev/smd10 | tee /etc/stress.txt &
for ((i=0; i< 1000; i++))
do
echo -e "\nRun number: $i\n"
#wait untill module restart and bee ready for next restart
dmesg | grep ERROR
echo -e 'AT+CFUN=1,1\r\n' > /dev/smd10
echo -e "\nADB device booted successfully\n"
done
I want to restart module 1000 times using this script.
Module is like android device witch has linux inside it. But I use Windows.
AT+CFUN=1,1 - reset
When I push script, after every restart I need a command which will wait module and start up again and execute script 1000 times. Then I do pull in .txt file and save all output content.
Which command should I use?
I try commands like wait, sleep, watch, adb wait-for-device, ps aux | grep... Nothing works.
Can someone help me with this?
I find the solution. This is how my script actually looks:
#!/bin/bash
cat /dev/smd10 &
TEST=$(cat /etc/output.txt)
RESTART_TIMES=1000
if [[ $TEST != $RESTART_TIMES ]]
then
echo $((TEST+1)) > /etc/output.txt
dmesg
echo -e 'AT+CFUN=1,1\r\n' > /dev/smd10
fi
These are the steps that you need to do:
adb push /path/to/your/script /etc/init.d
cd /etc
cat outputfile.txt - make an output file and write inside file 0 ( echo 0 > output.txt )
cd init.d
ls - you should see rc5.d
cd .. then cd rc5.d - go inside
ln -s ../init.d/yourscript.sh S99yourscript.sh
ls - you should see S99yourscript.sh
cd .. return to init.d directory
chmod +x yourscript.sh - add permision to your script
./yourscript.sh

"tee" allows us to redirect output from a command - is there any simple way to output the command string being run itself too?

When looking at log files many times I wonder what options/parameters did I use when running a given command that generated this log file.
Is there any simple way to output the command string being run itself too into stdout to be redirected into the log file at the top of the file?
You can echo the command, tee it to a file, pipe it to sh, then append the output to the file with tee -a myfile:
echo "ls -la" | tee myfile | sh | tee -a myfile
On stdout, you see just the output of the command, but in myfile, the first line is ls -la and the following lines contain the output of the command.
In the script that write to the log file, use set -x, which prints into STDERR the commands that are executed. Use set +x to reset to the default behavior (no printing of the commands).
Example:
Script test.sh:
#!/usr/bin/env bash
echo "INFO: test log message 1"
set -x
echo "INFO: test log message 2"
set +x
echo "INFO: test log message 3"
Run the script:
./test.sh 1>test.log 2>&1
Output in test.log:
INFO: test log message 1
+ echo 'INFO: test log message 2'
INFO: test log message 2
+ set +x
INFO: test log message 3
SEE ALSO:
Debugging Bash scripts

How to save linux command line outputs to a .txt file?

I would like to make a .sh executable that inputs a terminal command, and saves the output to a .txt file.
For example, take the output of i2cdump and save to a file. The terminal commands for this are
i2cdump -r -y 0x0-6 0 0x68
"outputs the specified bytes 0x0-6 to terminal window"
How can I use a .sh executable to do this automatically, and save the output to a file stored in /dir/?
Some pseudo code I have for myfile.sh:
#!/bin/bash
output=$(i2cdump -r -y 0x0-6 0 0x68)
FILE * fp;
// write output to file
// save to directory
close(fp)
You can using shell redirection like so:
echo "Hello world" > greetings.txt
Or to suit your needs:
#!/bin/bash
i2cdump -r -y 0x0-6 0 0x68 > output.txt
# closing is automatic at the end of redirecting.
General information about shell redirection of standart output stream in Bash manual: Redirections
Any command can have > appended to it to redirect the output.
As in:
echo "foo" > /path/to/file
Note, there are two things you should know:
1 ">" is overwriting a file while >> is appending.
As in:
echo "foo" > /path/to/file
file content will be:
foo
while
echo "foo" > /path/to/file
echo "foo2" >> /path/to/file
file content will be:
foo
foo2
And also, if you want to redirect errors you can use the 2 operand.
As in:
cat /path/to/non-existing-file 2> /outputfile
Will write all the operation's error into outputfile.
The same > and >> logic applies.

Shell script - Output to both the terminal and a log file in a sub-shell

I have a few shell scripts that are intended to work together. The first script (script1.sh) calls the next script in a sub-shell. The second script (script2.sh) needs to "return" something for the first script to use. I need the last line that is echoed in the second script. However, when I use it this way, any output via echo in the second script does not get output to the terminal. I want all output from the first and second (and third, and fourth, ...) to be output to the terminal, but also written to a log file.
script1.sh:
#!/bin/sh
func_one() {
RESULT=$(./script2.sh | tail -1)
echo "RESULT: $RESULT"
}
func_one | tee log_file.log
script2.sh:
#!/bin/sh
echo "Hello"
echo "World!"
Attempt 1 output:
$ ./script1.sh
RESULT: World!
$
log_file.log contents:
RESULT: World!
If I try to redirect output in the second script, then it outputs to the terminal, but not to the log file:
script2.sh:
#!/bin/sh
echo "Hello" >&2
echo "World!" >&2
Attempt 2 output:
$ ./script1.sh
Hello
World!
RESULT:
log_file.log contents:
RESULT:
I also tried outputting to terminal and tee on the same line in script 1:
func_one >&2 | tee log_file.log
But that gives the same result as the first attempt.
What I would like is to have both output to the terminal AND written to the .log file: (if it was working correctly)
$ ./script1.sh
Hello
World!
RESULT: World!
$
log_file.log contents:
Hello
World!
RESULT: World!
How can I go about getting this result? Also, it would be preferred to NOT use bash, as a few of our machines we are going to be running this on do not have bash.
I've looked here:
How do I get both STDOUT and STDERR to go to the terminal and a log file?
but that didn't help in my case.
To get all the output of script2.sh sent to the terminal without interfering with the work of script1.sh, try this modification of script1.sh:
$ cat script1.sh
#!/bin/bash
func_one() {
RESULT=$(./script2.sh | tee >(cat >&2) | tail -1)
echo "RESULT: $RESULT"
}
func_one | tee log_file.log
Here, the first tee command makes sure that all script2.sh output appears, via stderr, on the terminal. To do this, process substitution is needed (and this, in turn, requires an upgrade from sh to bash).
The output is:
$ ./script1.sh
Hello
World!
RESULT: World!
Variation
This is the same as the above except that we don't touch stderr (you may want to reserve that errors). Here, we create an additional file descripter, 3, to duplicate stdout:
#!/bin/bash
exec 3>&1
func_one() {
RESULT=$(./script2.sh | tee >(cat >&3) | tail -1)
echo "RESULT: $RESULT"
}
func_one | tee log_file.log

How do I write standard error to a file while using "tee" with a pipe?

I know how to use tee to write the output (standard output) of aaa.sh to bbb.out, while still displaying it in the terminal:
./aaa.sh | tee bbb.out
How would I now also write standard error to a file named ccc.out, while still having it displayed?
I'm assuming you want to still see standard error and standard output on the terminal. You could go for Josh Kelley's answer, but I find keeping a tail around in the background which outputs your log file very hackish and cludgy. Notice how you need to keep an extra file descriptor and do cleanup afterward by killing it and technically should be doing that in a trap '...' EXIT.
There is a better way to do this, and you've already discovered it: tee.
Only, instead of just using it for your standard output, have a tee for standard output and one for standard error. How will you accomplish this? Process substitution and file redirection:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Let's split it up and explain:
> >(..)
>(...) (process substitution) creates a FIFO and lets tee listen on it. Then, it uses > (file redirection) to redirect the standard output of command to the FIFO that your first tee is listening on.
The same thing for the second:
2> >(tee -a stderr.log >&2)
We use process substitution again to make a tee process that reads from standard input and dumps it into stderr.log. tee outputs its input back on standard output, but since its input is our standard error, we want to redirect tee's standard output to our standard error again. Then we use file redirection to redirect command's standard error to the FIFO's input (tee's standard input).
See Input And Output
Process substitution is one of those really lovely things you get as a bonus of choosing Bash as your shell as opposed to sh (POSIX or Bourne).
In sh, you'd have to do things manually:
out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"
Simply:
./aaa.sh 2>&1 | tee -a log
This simply redirects standard error to standard output, so tee echoes both to log and to the screen. Maybe I'm missing something, because some of the other solutions seem really complicated.
Note: Since Bash version 4 you may use |& as an abbreviation for 2>&1 |:
./aaa.sh |& tee -a log
This may be useful for people finding this via Google. Simply uncomment the example you want to try out. Of course, feel free to rename the output files.
#!/bin/bash
STATUSFILE=x.out
LOGFILE=x.log
### All output to screen
### Do nothing, this is the default
### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1
### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1
### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)
### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)
### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}
### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)
### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}
### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
In other words, you want to pipe stdout into one filter (tee bbb.out) and stderr into another filter (tee ccc.out). There is no standard way to pipe anything other than stdout into another command, but you can work around that by juggling file descriptors.
{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
See also How to grep standard error stream (stderr)? and When would you use an additional file descriptor?
In bash (and ksh and zsh), but not in other POSIX shells such as dash, you can use process substitution:
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
Beware that in bash, this command returns as soon as ./aaa.sh finishes, even if the tee commands are still executed (ksh and zsh do wait for the subprocesses). This may be a problem if you do something like ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. In that case, use file descriptor juggling or ksh/zsh instead.
To redirect standard error to a file, display standard output to the screen, and also save standard output to a file:
./aaa.sh 2>ccc.out | tee ./bbb.out
To display both standard error and standard output to screen and also save both to a file, you can use Bash's I/O redirection:
#!/bin/bash
# Create a new file descriptor 4, pointed at the file
# which will receive standard error.
exec 4<>ccc.out
# Also print the contents of this file to screen.
tail -f ccc.out &
# Run the command; tee standard output as normal, and send standard error
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out
# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
If using Bash:
# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect
# Redirect standard error and out together
% cmd >stdout-redirect 2>&1
# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2
Credit (not answering from the top of my head) goes here: Re: bash : stderr & more (pipe for stderr)
If you're using Z shell (zsh), you can use multiple redirections, so you don't even need tee:
./cmd 1>&1 2>&2 1>out_file 2>err_file
Here you're simply redirecting each stream to itself and the target file.
Full example
% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err
Note that this requires the MULTIOS option to be set (which is the default).
MULTIOS
Perform implicit tees or cats when multiple redirections are attempted (see Redirection).
Like the accepted answer well explained by lhunath, you can use
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Beware than if you use bash you could have some issue.
Let me take the matthew-wilcoxson example.
And for those who "seeing is believing", a quick test:
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Personally, when I try, I have this result:
user#computer:~$ (echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
user#computer:~$ Test Out
Test Err
Both messages do not appear at the same level. Why does Test Out seem to be put like if it is my previous command?
The prompt is on a blank line letting me think the process is not finished, and when I press Enter this fix it.
When I check the content of the files, it is ok, and redirection works.
Let’s take another test.
function outerr() {
echo "out" # stdout
echo >&2 "err" # stderr
}
user#computer:~$ outerr
out
err
user#computer:~$ outerr >/dev/null
err
user#computer:~$ outerr 2>/dev/null
out
Trying again the redirection, but with this function:
function test_redirect() {
fout="stdout.log"
ferr="stderr.log"
echo "$ outerr"
(outerr) > >(tee "$fout") 2> >(tee "$ferr" >&2)
echo "# $fout content: "
cat "$fout"
echo "# $ferr content: "
cat "$ferr"
}
Personally, I have this result:
user#computer:~$ test_redirect
$ outerr
# stdout.log content:
out
out
err
# stderr.log content:
err
user#computer:~$
No prompt on a blank line, but I don't see normal output. The stdout.log content seem to be wrong, and only stderr.log seem to be ok.
If I relaunch it, the output can be different...
So, why?
Because, like explained here:
Beware that in bash, this command returns as soon as [first command] finishes, even if the tee commands are still executed (ksh and zsh do wait for the subprocesses)
So, if you use Bash, prefer use the better example given in this other answer:
{ { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2
It will fix the previous issues.
Now, the question is, how to retrieve exit status code?
$? does not work.
I have no found better solution than switch on pipefail with set -o pipefail (set +o pipefail to switch off) and use ${PIPESTATUS[0]} like this:
function outerr() {
echo "out"
echo >&2 "err"
return 11
}
function test_outerr() {
local - # To preserve set option
! [[ -o pipefail ]] && set -o pipefail; # Or use second part directly
local fout="stdout.log"
local ferr="stderr.log"
echo "$ outerr"
{ { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2
# First save the status or it will be lost
local status="${PIPESTATUS[0]}" # Save first, the second is 0, perhaps tee status code.
echo "==="
echo "# $fout content :"
echo "<==="
cat "$fout"
echo "===>"
echo "# $ferr content :"
echo "<==="
cat "$ferr"
echo "===>"
if (( status > 0 )); then
echo "Fail $status > 0"
return "$status" # or whatever
fi
}
user#computer:~$ test_outerr
$ outerr
err
out
===
# stdout.log content:
<===
out
===>
# stderr.log content:
<===
err
===>
Fail 11 > 0
In my case, a script was running command while redirecting both stdout and stderr to a file, something like:
cmd > log 2>&1
I needed to update it such that when there is a failure, take some actions based on the error messages. I could of course remove the dup 2>&1 and capture the stderr from the script, but then the error messages won't go into the log file for reference. While the accepted answer from lhunath is supposed to do the same, it redirects stdout and stderr to different files, which is not what I want, but it helped me to come up with the exact solution that I need:
(cmd 2> >(tee /dev/stderr)) > log
With the above, log will have a copy of both stdout and stderr and I can capture stderr from my script without having to worry about stdout.
The following will work for KornShell (ksh) where the process substitution is not available,
# create a combined (standard input and standard output) collector
exec 3 <> combined.log
# stream standard error instead of standard output to tee, while draining all standard output to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3
# cleanup collector
exec 3>&-
The real trick here, is the sequence of the 2>&1 1>&3 which in our case redirects the standard error to standard output and redirects the standard output to file descriptor 3. At this point the standard error and standard output are not combined yet.
In effect, the standard error (as standard input) is passed to tee where it logs to stderr.log and also redirects to file descriptor 3.
And file descriptor 3 is logging it to combined.log all the time. So the combined.log contains both standard output and standard error.
Thanks lhunath for the answer in POSIX.
Here's a more complex situation I needed in POSIX with the proper fix:
# Start script main() function
# - We redirect standard output to file_out AND terminal
# - We redirect standard error to file_err, file_out AND terminal
# - Terminal and file_out have both standard output and standard error, while file_err only holds standard error
main() {
# my main function
}
log_path="/my_temp_dir"
pfout_fifo="${log_path:-/tmp}/pfout_fifo.$$"
pferr_fifo="${log_path:-/tmp}/pferr_fifo.$$"
mkfifo "$pfout_fifo" "$pferr_fifo"
trap 'rm "$pfout_fifo" "$pferr_fifo"' EXIT
tee -a "file_out" < "$pfout_fifo" &
tee -a "file_err" < "$pferr_fifo" >>"$pfout_fifo" &
main "$#" >"$pfout_fifo" 2>"$pferr_fifo"; exit
Compilation errors which are sent to standard error (STDERR) can be redirected or save to a file by:
Bash:
gcc temp.c &> error.log
C shell (csh):
% gcc temp.c |& tee error.log
See: How can I redirect compilation/build error to a file?

Resources