2

For documentation purposes, I mean to redirect to file stdout and stderr from a command I execute. For instance, I would run (my command is less trivial than the alias ll but it probably doesn't matter):

$ ll > out-err.dat 2>&1
$ cat out-err.dat
drwxr-xr-x 39 us00001 us00001    4096 jul 31 14:57 ./
drwxr-xr-x  3 root    root       4096 feb  2 06:06 ../
-rw-------  1 us00001 us00001   62226 jul 31 11:56 .bash_history
...

Also for documentation purposes, I want to store in the same output file the command line I used. The intended behaviour and output is

$ [NEW COMMAND LINE]?
$ cat out-err.dat
[NEW COMMAND LINE]   <- This first line would contain the command line used
drwxr-xr-x 39 us00001 us00001    4096 jul 31 14:57 ./
drwxr-xr-x  3 root    root       4096 feb  2 06:06 ../
-rw-------  1 us00001 us00001   62226 jul 31 11:56 .bash_history
...

How can this be done? I know I could write a bash script and execute it, so the command would remain documented separately. I could further write a script to echo the command line to file and then execute it with redirection to the same file. I am looking for a possible script-less solution.


EDIT: Feedback on a nice answer. This wouldn't fit as a comment.
I tested with command echo_command ll echo_command.sh ../dir > out-err.dat 2>&1.
Script echo_command.sh, which I source, contains the definitions of the functions.
../dir is a non-existing dir, to force some output to stderr.

Method 1: Works nice, except for two issues:

  • It doesn't understand aliases (ll in this case; when replacing with ls it worked).

  • It doesn't record the redirection part.

Method 2: It doesn't work that well. Now the redirection part is also printed, but the command line is printed to screen instead of redirected to file.


EDIT: Feedback on a comment posted, about a script utility. It is quite versatile, even more so with scriptreplay.
script can be called alone, which produces an interactive shell (it wouldn't keep the recent history of the parent shell)
It can also be called as script -c <command> <logfile>. This last form corresponds with the objective of the OP, but it doesn't store the command itself into the log file. It produces (at least in base cases) the same output as <command> > <logfile> 2>&1.
So it seems this is not useful here.

  • 5
    the script utility might work for you. it is used to record terminal sessions, including STDOUT and STDERR, and it captures all the commands you run. tecmint.com/… – Tim Kennedy Jul 31 '18 at 21:15
  • 2
    can't you just run set -x? – DopeGhoti Jul 31 '18 at 22:27
  • @TimKennedy - script appears not useful here, see updated OP. – sancho.s Aug 3 '18 at 8:32
  • @DopeGhoti - I do not see how set -x would achieve the intended purpose. It merely replicates the command to screen, not to the log file... plus there are a few issues (aliases are replaced, the redirection part is not printed, echo for the prompt is appended, ...) – sancho.s Aug 3 '18 at 8:39
2

You could use a function like this:

echo_command() { printf '%s\n' "${*}"; "${@}"; }

Example:

$ echo_command echo foo bar baz
echo foo bar baz
foo bar baz
$ echo_command uname
uname
Linux

As Tim Kennedy said, there's also a very useful script command:

$ script session.log
Script started, file is session.log
$ echo foo
foo
$ echo bar
bar
$ echo baz
baz
$ exit
exit
Script done, file is session.log
$ cat session.log
Script started on 2018-07-31 16:30:31-0500
$ echo foo
foo
$ echo bar
bar
$ echo baz
baz
$ exit
exit

Script done on 2018-07-31 16:30:43-0500

Update

If you also need to log the redirections and basically every shell syntax (note that I added a little Command line: message to easily identify the command being executed):

echo_command() {
  local arg
  for arg; do
    printf 'Command line: %s\n' "${arg}"
    eval "${arg}"
  done
}

Just take into account that you should be very careful with the quoting as eval is used:

$ echo_command 'echo foo > "file 2"; cat "file 2"'
Command line: echo foo > "file 2"; cat "file 2"
foo

It also accepts many commands at once instead of only one:

$ echo_command 'echo foo' 'echo bar' 'echo baz'
Command line: echo foo
foo
Command line: echo bar
bar
Command line: echo baz
baz
  • Your function works nicely, even with redirectors as I need (the part of the command line that redirects is not printed; I wanted that also, but it is a minor issue). E.g., echo_command cat todo.txt todo2.txt > test.dat 2>&1 produces a file test.dat whose first line is cat todo.txt todo2.txt – sancho.s Jul 31 '18 at 21:43
  • @sancho.s Just updated the answer. Pass the redirections as part of the arguments and they will be logged. – nxnev Jul 31 '18 at 21:56
  • Please see feedback added at the bottom of the OP. – sancho.s Aug 3 '18 at 8:06
1

You could use the xtrace mechanism where the shell prints the commands it's executing to stdout:

(set -x; ls -l) > out-err.dat 2>&1

Change $PS4 from its default of "+ " (in most shells) to anything else if you don't like it (make it empty if you don't want a prefix).

As a function:

log_into() ( # args: output-file command args
   exec > "$1" 2>&1
   shift
   PS4='Running: '
   set -o xtrace
   "$@"
)

log_into out-err.dat ls -l
  • The first form works fine, with minor issues: 1) It replaces aliases, 2) It prepends the annoying "+ ", 3) It doesn't store the redirection part. The second form works fine, with issues: 1) It messes up with aliases, 2) It doesn't store the redirection part. – sancho.s Aug 3 '18 at 8:48

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.