Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to make the output from one bash command go to a user selected location?

I'm writing a bash script that amongst other things, starts a user specified command, sending the output to either file, file and stdout, or just stdout as required.

I don't like the duplication of the line that runs the user command which has different endings depending on the output type wanted.

#!/bin/bash

# messy but works
function my_function() {

    local pid_file=$1; shift
    local logfile=$1; shift
    local tee_output=$1; shift
    local cmd=$*; shift

    # do some stuff

    { # codeblock to allow redirection of all content by default to somewhere different
    
        # do some stuff
    
        # This IF Block runs the user specified cmd. Output will go to different locations as specified
        if [ "$logfile" = "NO_LOG" ] ; then
            # ALL Output -> default stdout and default stderr
            sh -c "echo \$\$ > '$pid_file'; $cmd" 1>&3 2>&4 &
        else
            if [ "$tee_output" -eq 1 ] ; then
                    # ALL Output -> logfile AND default stdout and default stderr
                    sh -c "echo \$\$ > '$pid_file'; $cmd" 1>&3 2>&4 | tee "$logfile" &
                else
                    # ALL Output -> logfile
                    sh -c "echo \$\$ > '$pid_file'; $cmd" >> "$logfile" &
                fi
        fi
        
        # do some stuff
        
    } >> "script_output.log" 2>&1 ; # This will redirect stderr and stdout for this codeblock to file
    
    # do some stuff
    
}

exec 3>&1 4>&2 ; # make a copy of the default stdout and stderr destinations
my_function cmd.pid output.log echo hi

Is there a way of rewriting this horrible IF block so that the various options are appended to the end of the sh -c "echo \$\$ > '$pid_file'; $cmd" part that remains constant?

I think something like the following statement might be the answer, but I can't quite get it working. Can you show me a neat way of doing this please? Thanks.

# possible idea for a neater solution (not working)
sh -c "echo \$\$ > '$pid_file'; $cmd" $(if [ "$logfile" = "NO_LOG" ] ; then
            echo '1>&3 2>&4 &' ;# send output to console
          else
            if [ "$tee_output" -eq 1 ] ; then
                echo '1>&3 2>&4 | tee $logfile &' ; # send output to console AND logfile
            else
                echo '>> "$logfile" &' ; # send output to logfile
            fi
          fi)
like image 928
extorn Avatar asked Jan 19 '26 08:01

extorn


1 Answers

Overall, it's exec this>there everywhere.

#!/bin/bash
my_function() {
    local pid_file=$1; logfile=$2
    local tee_output=0
    if [ "$3" = "tee" ] ; then
        tee_output=1
        shift
    fi
    cmd=("${@:3}")
    {
        echo "executing ${cmd[@]}"
        if [[ "$logfile" = "NO_LOG" ]]; then
            exec 1>&3 2>&4
        else
            if [ "$tee_output" = "1" ] ; then
                    exec 1>&3 2>&4
                    exec 1> >(tee "$logfile")
                else
                    exec >>"$logfile"
                fi
        fi
        
        echo "$BASHPID" > "$pid_file"
        exec "${cmd[@]}"
    } >>"script_output.log" 2>&1    
}
exec 3>&1 4>&2
my_function cmd.pid output.log echo hi

Also, service managers is an old topic. Consider just using systemd or systemd-run nowadays.

like image 175
KamilCuk Avatar answered Jan 21 '26 08:01

KamilCuk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!