Home > Back-end >  How can I pipe output, from a command in an if statement, to a function?
How can I pipe output, from a command in an if statement, to a function?

Time:04-09

I can't tell if something I'm trying here is simply impossible or if I'm really lacking knowledge in bash's syntax. This is the first script I've written.

I've got a Nextcloud instance that I am backing up daily using a script. I want to log the output of the script as it runs to a log file. This is working fine, but I wanted to see if I could also pipe the Nextcloud occ command's output to the log file too.

I've got an if statement here checking if the file scan fails:

if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

This works fine and I am able to handle the error if the system cannot execute the command. The error string above is sent to this function:

Print()
{
    if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
        echo "$1" | tee -a "$log_file"
    elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
        echo "$1" >> "$log_file"
    elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
        echo "$1"
    fi
}

How can I make it so the output of the occ command is also piped to the Print() function so it can be logged to the console and log file?

I've tried piping the command after ! using | Print without success.

Any help would be appreciated, cheers!

CodePudding user response:

The Print function doesn't read standard input so there's no point piping data to it. One possible way to do what you want with the current implementation of Print is:

if ! occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

Print "'occ' output: $occ_output"

Since there is only one line in the body of the if statement you could use || instead:

occ_output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1) \
    || Print "Error: Failed to scan files. Are you in maintenance mode?"

Print "'occ' output: $occ_output"

The 2>&1 causes both standard output and error output of occ to be captured to occ_output.

Note that the body of the Print function could be simplified to:

[[ $quiet_mode == No ]] && printf '%s\n' "$1"
(( logging ))           && printf '%s\n' "$1" >> "$log_file"

See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I replaced echo "$1" with printf '%s\n' "$1".

CodePudding user response:

How's this? A bit unorthodox perhaps.

Print()
{
    case $# in
      0) cat;;
      *) echo "$@";;
    esac |
    if [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "No" ]; then
        tee -a "$log_file"
    elif [[ "$logging" -eq 1 ]] && [ "$quiet_mode" = "Yes" ]; then
        cat >> "$log_file"
    elif [[ "$logging" -eq 0 ]] && [ "$quiet_mode" = "No" ]; then
        cat
    fi
}

With this, you can either

echo "hello mom" | Print

or

Print "hello mom"

and so your invocation could be refactored to

if ! sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all; then
    echo "Error: Failed to scan files. Are you in maintenance mode?"
fi |
Print

The obvious drawback is that piping into a function loses the exit code of any failure earlier in the pipeline.

For a more traditional approach, keep your original Print definition and refactor the calling code to

if output=$(sudo -u "$web_user" "$nextcloud_dir/occ" files:scan --all 2>&1); then
    : nothing
else
    Print "error $?: $output"
    Print "Error: Failed to scan files. Are you in maintenance mode?"
fi

I would imagine that the error message will be printed to standard error, not standard output; hence the addition of 2>&1

I included the error code $? in the error message in case that would be useful.

CodePudding user response:

Sending and receiving end of a pipe must be a process, typically represented by an executable command. An if statement is not a process. You can of course put such a statement into a process. For example,

echo a | ( 
  if true
  then
    cat
  fi )

causes cat to write a to stdout, because the parenthesis put it into a child process.

  • Related