How redirect script output from inside of script?

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