Is there a way to use `script` with shell functions? (color output)

I am using a script to run multiple tests (npm, python, etc.)

They have colored outputs.

I'm actually doing some of these tests while dispatching processes in parallel in the background and capturing the output in a variable that will be displayed when it's done (as opposed to allowing the output to TTY and mixing multiple outputs at the same time).

Everything works well, but the result is not colored and I would like to keep the colors. I understand that this is because it is not a TTY output, so the color is taken away and I was looking for tricks to avoid this.

See this answer: Is it possible to print a highlighted file using a shell redirect?

offers a way to do it, but doesn't work with shell functions

If I do this:

OUTPUT=$(script -q /dev/null npm test | cat)
echo -e $OUTPUT

      

I am getting the output in a variable and the command output echo

is colored.

but if f:

function run_test() { npm test; }
OUTPUT=$(script -q /dev/null run_test | cat)
echo -e $OUTPUT

      

I get:

script: run_test: No such file or directory

If I call the function run_test

passing it to the script as:

function run_test() { npm test; }
OUTPUT=$(script -q /dev/null `run_test` | cat)
echo -e $OUTPUT

      

it's like passing an output that has already been evaluated without colors, so the script's output is not colored.

Is there a way to get the shell functions to work with script

?

I could call the script call in a function like:

function run_test() { script -q /dev/null npm run test | cat; }

      

but there are several problems with this:

  • Sometimes I need to run multiple commands multiple times and send them in the background to run in parallel, this gets messy: I want to wrap the sequences in a shell function and run it with a script.
  • This would have already run the function, and will return when done. I want to pass a function to call another function that runs in the background and logs the output with a script.

PS: I also tried to npm config set color always

get npm to always output colors, but that doesn't seem to help, plus I have other functions that call, these are not all npm, so it won't work anyway.

+3


source to share


2 answers


You can use a program such as unbuffer

that simulates TTY to get color output from the software, the output of which eventually goes to the pipeline.

When:

unbuffer npm test | cat

      

... there the TTY is simulated unbuffer

so it doesn't see the FIFO going to cat

on its output.



If you want to run a shell function behind this type of shim, be sure to export it to the environment as in export -f

.


Demonstration of using this function with a shell function:

myfunc() { echo "In function"; (( $# )) && { echo "Arguments:"; printf ' - %s\n' "$@"; }; }
export -f myfunc
unbuffer bash -c '"$@"' _ myfunc "Argument one" "Argument two"

      

+2


source


I've tried unbuffer

and doesn't seem to work with shell functions

The script doesn't work passing it a shell function, however it is possible to pass some input of type STDIN, so I ended up working with

script -q /dev/null <<< "run_test"

      

or

echo "run_test" | script -q /dev/null

      

so I could pass this to a shell variable even using the variable as a COMMAND like:

OUTPUT=$(echo "$COMMAND" | script -q /dev/null)

      

and then print the colored output with



echo -e $OUTPUT

      

Unfortunately, this still prints out unnecessary garbage (i.e. shell name, command name, and command exit

at the end.

Since I wanted to capture the output code, I couldn't skip the output somewhere else, so I went like this:

run() {
    run_in_background "$@" &
}    

run_in_background() {    
        COMMAND="$@" # whatever is passed to the function    
        CODE=0

        OUTPUT=$(echo "$COMMAND" | script -q /dev/null) || CODE=$(( CODE + $? ));

        echo -e $OUTPUT | grep -v "bash" | grep -v "$COMMAND";
        if [ "$CODE" != "0" ]; then exit 1; fi
    }

      

and use like:

# test suites shell functions
run_test1() { npm test; }
run_test2() { python manage.py test; }

# queue tests to run in background jobs
run run_test1
run run_test2
# wait for all to finish
wait

      

I skip the part where I catch the errors and propagate the failure to the top PID, but you get the gist.

0


source







All Articles