5

My goal is to duplicate, redirect all output (stdout and stderr) of an application (apt-get) to a file while retaining the usual behavior of the application (apt-get), stdout and stderr.

How to create a real copy of file descriptor stdout and stderr? I would like to keep stdout and stderr as "natural" as possible. I.e. if it was running for example apt-get, I would wish to retain colors and progress information. [1]

Here is an example. (The wrapper does more stuff, just kept it simple.)

#!/bin/bash

temp_dir="$(mktemp --directory)"
logfile="$temp_dir/log"

unbuffer apt-get "$@" 1> >(tee -a "$logfile") 2> >(tee -a "$logfile" >&2)

Is it possible to achieve this without unbuffer / script / tee just with bash built-ins?

(To keep the wrapper simpler and dependent on less external binaries. Otherwise this could cause conflicts with mandatory access control and so forth.)

(Usage of script(1) in non-interactive scripts is discouraged by its man page.)


[1]

22% [8 Packages 3,449 kB/7,098 kB 49%]
174 kB/s 4min 57s
  • I don't have an Ubuntu box handy to check, but the flag to look for would be some sort of --color=always flag for apt-get. – Wildcard Feb 27 '17 at 22:32
  • tee is a POSIX utility. If you can't use tee then you're less likely to be able to use mktemp and apt-get. The tee utility does not buffer its output. – Kusalananda Mar 8 '17 at 22:35
  • Again: If you can run apt-get (with everything that it does), you can most certainly use tee. – Kusalananda Sep 24 '17 at 19:28
  • The most generic way to ask the question is How to create a real copy of file descriptor stdout and stderr without using unbuffer / script / tee just with bash built-ins?. The question isn't tied to apt-get. It's merely an example. unbuffer / script / tee are available, but I don't want to use it. Because as said, it causes follow up issues with mandatory access control and more. This is a shell script / bash scripting question. If it's possible to create a real copy of file descriptions stdout and stderr at once without external tools. – adrelanos Sep 25 '17 at 8:45
  • You would have to implement your own tee, probably in C or some lower level language. – Kusalananda Jul 29 '18 at 17:39
0

This POSIX shell fake tee function has no dependencies:

faketee() {
   if [ "$1" = '-a' ] ; then 
      shift
      while read -r x ; do 
         echo "$x" >> "$1"
         echo "$x"
      done
   else  
      while read -r x ; do 
         echo "$x" > "$1"
         echo "$x"
      done
   fi
}

Test it in bash with ls on a file that exists, (which outputs one line to STDOUT), and a file name that does not, (which outputs another line to STDERR):

cd /tmp
ls /bin/bash /bin/nosuchfile  1>  >(faketee -a std.log) \
                              2>  >(faketee -a err.log >&2)

Output:

/bin/bash
ls: cannot access '/bin/nosuchfile': No such file or directory

...then run it again with two more such file names:

ls /bin/dash /bin/nohowsuchfile  1>  >(faketee -a std.log) \
                                 2>  >(faketee -a err.log >&2)

Output:

/bin/dash
ls: cannot access '/bin/nohowsuchfile': No such file or directory

Now check the log files to see if the -append switch works:

grep -n '.' std.log err.log

Output indicates appended redirects are where they should be:

std.log:1:/bin/bash
std.log:2:/bin/dash
err.log:1:ls: cannot access '/bin/nosuchfile': No such file or directory
err.log:2:ls: cannot access '/bin/nohowsuchfile': No such file or directory
  • Didn't try to fake unbuffer yet... – agc May 11 at 1:48
  • Both sides of the if are the same code but for one extra >. This seems inelegant, but I haven't found a way around it that wouldn't require putting an if, (or an eval), in the while loop itself, which would be inefficient. – agc May 11 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.