Is it possible to redirect a script output by command inside of that script?
I mean, if I have a script 'dosome.sh' I could run it by
>dosome.sh > dosome.log
I would dream to get some command inside of scrip to do the same; so, running the dosome.sh would have all output redirected to a log file
(Do not advise, please, put redirection to a log file from every command of that script. It is already unrealistical.)
So I would image something like that:
#! /usr/bin/bash
<redirection command>
....
.... script body
....
# end of script
Iven would be greate to be able to do it the same as it would be done with 'tee' command: to have the output be written to file and in the same time to standard output, to screen, like in this command:
>dosome.sh | tee dosome.log
Appreciate any advice!
I could do that with "exec", but I couldn't get that work to write to a file and stdout simultaneously,
Here is the snippet,
#!/bin/bash
exec 1>/tmp/log
log() {
echo `date` : "$@"
}
log "Start"
echo "Process"
ls -l
log "Done Logging"
Please try exec options,
Thanks,
Nagarajan G
Thank you Nagarajan G!!
Exec works perfect to redirect from inside!
I try to get the print-out simultaneously and trying to do it with 'tail -f'
And I've get rock-n-roll:
to start 'tail' need and continue - need to run tail background (... & )
to stop 'tail' when it is done - need to know it's PID
but it is bad idea to kill 'tail' on end of screept - it could exit in a midle
so need to run monitoring of the script to stop the tail rigt after t'tail' start
Is this seems doable? Because by now I have some problem to get PID and kill it. (Actually I did it from outside script and get mess with PIDs)
Any oppinion?
Hi,
I managed to get that working using exec & pipes.The following is the script which i hope to work for you as well,
#!/bin/bash
#Objective : To redirect the stdout & stderr to two different files from within the script and without sacrificing the scripts output
mkfifo /tmp/out.pipe /tmp/err.pipe
exec 3>&1 4>&1
tee /tmp/std.out < /tmp/out.pipe >&3 &
pid_out=$!
exec 1>/tmp/out.pipe
tee /tmp/std.err < /tmp/err.pipe >&4 &
pid_err=$!
exec 2>/tmp/err.pipe
log() {
echo "`date` : $@ "
}
log "Starting the process"
log "Listing directories now..."
ls -l
ls -l Log_to_Stderr
exec 1>&3 3>&- 2>&4 4>&-
wait $pid_out
wait $pid_err
rm /tmp/out.pipe /tmp/err.pipe
log "End of Processing"
Please let us know if you could find a better approach
Thanks
Nagarajan G
1 Like
Once again, thank you very much for answering and for your time and effort!
Your solution is very interesting, although it is over my knowledge. I will review it in detail tomorrow.
By now I have briefly tried to execute it. It is works, but I do not get where are resulting log files.
I've did commented the 'rm ..' command, but those /tmp/ file are pipes (that I do not understand well, but tomorrow..) and after process is done they are 0-size.
I also have get something to work, but my code is far bigger and I did not take care the error-stream.
I've prepared a peace of code that should be added to the beginning of a script.
One benefit, it seems, it has (if I did not mis-understand your code completely,) that it does not require a script to reach the end. The script could exit in the meddle or could be killed or stopped by any other way.
This is what I have:
#! /usr/bin/bash
shopt -s expand_aliases
alias ec=/usr/bin/echo
typeset log_fl=`basename $0`_` date +'%H%M%S'`.log
# sending this script output to the log file
exec 1>${log_fl}
terml=`tty`;
if [ -z "`ec $terml|grep "/dev/"`" ]
then terml="";
fi; # if 'tty' returns not a device make it empty
ec " === output terminal is : >$terml< === ";
ec " === current script PID=$$ === "
ec " === log file is \n$log_fl === "
# starting to print on screen the ${log_fl} - must be killed by the end of the script
if [ -z ${terml} ]; then
tail -f ${log_fl} # not sure there is a situation to see the log file in this case
else
tail -f ${log_fl}>${terml} &
fi
# this function will be waiting the script completion and kill the 'tail -f ..'
watch_tail()
{
alias ec=/usr/bin/echo
out_dv=$1; # first parameter is a current proces's terminal, if presented
# to print out info depending on sent 'out_dv'; - parameters are message
out()
{
inf="\n////-- FROM tail-watcher: ---////\n";
inf=$inf" $*"
inf=$inf"\n\\\\\\\\\\\\\\\\-- FROM tail-watcher ---\\\\\\\\\\\\\\\\\n"
if [ -z "$out_dv" ]; then ec "$inf"; else ec "$inf" > ${out_dv}; fi
}
# looking for PID of this process only, with PPID=$$ - this process PID
cur_pid=$$; # PID of the current process - will be a second number in next 'ps..'
#the 'ps ' command use first 79 characters of command to display arguments in 'ps .. -o args'
tail_args=`ec "tail -f ${log_fl}"|cut -c1-79`
# the 'tail ..''s PID is:
pid=`ps -e -o pid,ppid,args |\
grep ${cur_pid} |\
grep "${tail_args}"|\
grep -v grep | nawk '{print $1}'`
if [ "$pid" = "" ]; then
out " == ${tail_args} is not running";
# if runing in interactive shell (from screen) - return, else - exit
if [ -z "$PS1" ]; then exit 0; else return 0; fi
else
out "===>> current 'tail -f ..' PID=$pid";
fi
ppid=2; # initialize to get the loop run, at least, once
# ppid = 1 is 'root' user ; that means the parent (this script) is completed
while [ "$ppid" != "1" ];do
let fz++; # fuze - to break after number of loops in any case (15 sec for 100)
if ((fz>=8000)); then out "\n\nbreaking the tail-watch loop by fuse \n\n"; break; fi
pids=`ps -e -o pid,ppid,args |\
grep "${tail_args}"|\
grep ${pid} |\
grep -v grep | nawk '{print $1"-"$2}'`;
if [ "$pids" = "" ]; then
out "\n\nbreaking the tail-watch loop by not founding the 'tail..'\n\n";
break;
fi; # the 'tail ..' is disapeared; ending loop
ppid=`ec ${pids} | nawk -F- '{print $2}'`
if ((ppid==1)); then
out "killing the tail -f; pid=$pid";
# wait couple second to give the tail complete output
sleep 3;
kill -9 $pid;
fi
done
}
# now starting this one background and, after that, processing the script actions
watch_tail $terml &
I will appreciate your oppinion and advice regarding that.
Alex