Search code examples
bashevalcdtee

eval commands with STDERR/STDOUT redirection causing problems


I'm trying to write a bash script where every command is passed through a function that evaluates the command using this line:

eval $1 2>&1 >>~/max.log | tee --append ~/max.log

An example of a case where it does not work is when trying to evaluate a cd command:

eval cd /usr/local/src 2>&1 >>~/max.log | tee --append ~/max.log

The part the causes the issue is the | tee --append ~/max.log part. Any idea why I'm experiencing issues?


Solution

  • From the bash(1) man page:

    Each command in a pipeline is executed as a separate process (i.e., in a subshell).

    Therefore, cd can not change the working directory of the current shell when used in a pipeline. To work around this restriction, the usual approach would be to group cd with other commands and redirect the output of the group command:

    {
        cd /usr/local/src
        command1
        command2
    } | tee --append ~/max.log
    

    Without breaking your existing design, you could instead handle cd specially in your filter function:

    # eval all commands (will catch any cd output, but will not change directory):
    eval $1 2>&1 >>~/max.log | tee --append ~/max.log
    # if command starts with "cd ", execute it once more, but in the current shell:
    [[ "$1" == cd\ * ]] && $1
    

    Depending on your situation, this may not be enough: You may have to handle other commands that modify the environment or shell variables like set, history, ulimit, read, pushd, and popd as well. In that case it would probably be a good idea to re-think the program's design.