0

I'm developing a BASH script which invokes another BASH script which prints a line to stdout. That output is captured by the first BASH script and used later. It works, but it has the downside that any other output which is printed by the second script will cause this part to behave unexpectedly, because there will be extra content.

main.sh

#!/bin/bash
# Invoke worker.sh and capture its standard output to stats
stats=$(worker.sh --generate-stats)
echo "stats=$stats"

worker.sh

#!/bin/bash
[[ $1 == "--generate-stats" ]] && echo "cpu=90 mem=50 disk=15"

In this over-simplified example, it's not a problem to use this construct, but as worker.sh grows in size and complexity, it's hard to remember that no other command can print to stdout without confounding the behavior, and if someone else works on worker.sh without realizing they can't print to stdout, it can easily get fouled. So what is considered good practice to generate output in one script and use it in the other?

I'm wondering if a fifo would be appropriate, or another file descriptor, or just a plain file. Or if exec should be used in this case, something like what is shown here https://www.tldp.org/LDP/abs/html/x17974.html:

#!/bin/bash
exec 6>&1       # Link file descriptor #6 with stdout.
                # Saves stdout.
exec >&2        # stdout now goes to stderr

echo "Didn't know I shouldn't print to stdout"

exec 1>&6 6>&-  # Restore stdout and close file descriptor #6.
[[ $1 == "--generate-stats" ]] && echo "cpu=90 mem=50 disk=15"

But I wouldn't want to use that if it's not considered good practice.

3
  • 6
    Provide clear documentation comments in the code. The alternative is to write the worker.sh to be sourced instead of executed -- this way any spurious output will just clutter the screen, not break the code. Commented Nov 2, 2018 at 14:51
  • 2
    Programs that print anything but output to stdout are buggy, full-stop. Print a status message to stdout? It's a bug. Print a prompt to stdout? It's a bug. So as opposed to needing to know that stdout is going to be captured, you should always assume that only actual intended content goes there (and if your program doesn't write anything that's expected to be treated or captured as output, it shouldn't use stdout at all). Commented Nov 2, 2018 at 15:00
  • Good practice would be to logically link the two scripts in your source control and testing system. If there is a dependancy of one script on another then if either script breaks the other it should fail its mandatory tests. If you don't have a testing system for scripts then all bets are off. Commented Nov 2, 2018 at 15:01

2 Answers 2

1

Many command-line utilities have quiet and verbose modes; it's generally considered good practice to have the most verbose output (debugging, tracing, etc.) be separated to standard error anyway, but it's common to have normal output be formatted for human legibility (e.g. include table headings and column separators) and quiet mode output be just the bare data for programmatic use. (For one example, see docker images vs docker images -q). So that would be my recommendation - have worker.sh take a flag indicating whether its output is being consumed programmatically, and write it such that its output is all sent via a function that checks that flag and filters appropriately.

Sign up to request clarification or add additional context in comments.

Comments

1

Maybe a different approach would be for the second script to test to see if it's stdout is being used programatically:

gash.sh:

#!/bin/bash
data=$(./another.sh)
echo "Received $data"

another.sh:

#!/bin/bash

# for -t see man isatty(3).  1 is file descriptor 1 - stdout
if [ -t 1 ]; then
    echo "stdout is a terminal"
else
    echo "stdout is not a terminal"
fi

Gives (where $ is a generic keyboard prompt):

$ bash gash.sh
Received stdout is not a terminal
$ bash another.sh
stdout is a terminal

You could then set a flag to change script behaviour (ls(1) does a similar thing). However, you should be prepared for this:

$ bash another.sh|more
stdout is not a terminal

$ bash another.sh > out.txt
$ cat out.txt
stdout is not a terminal

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.