5

I have a pretty large script ( functions contains around 4000 lines of code) . Here is a part of it :

#!/bin/bash 


. ./functions > /dev/null 2>&1

some_function(){

while true
do

CHOICE=$(whiptail --menu "\n\n\n\n\n\n\n\n" --title "Tools" --nocancel $window 20 \
"1" "Option1" \
"2" "Option2" \
"3" "Option3" 3>&1 1>&2 2>&3)


case $CHOICE in

    1)echo $1 $2 $3;;
    2)echo $2 $1 $3;;                                       
    3)echo $3 $2 $1;;

esac
done
}



while true; do 
arr=()
for ((n=1; n<=$node_num; n++))
        do
        node+=($n NODE$n)
done


OPTION=$(whiptail --menu "\n\n\n\nPlease choose:\n\n\n" --title "tools" $window 20 "${node[@]}" \

case $OPTION in

        1) some_function 1 2 3 ;;  
        2) some_function 2 1 3 ;;
        3) some_function 3 1 2 ;;
esac
done

I want to log the commands executed in the script.

What I have tried so far is :

  1. #!/bin/bash -x --> this will log all the output , but will also "spam" the logs with unneeded information like variable values etc. However this seems to be the best way so far...
  2. I have tried #!/bin/bash -i , enabling history with set -o history . The disadvantage of this is it will log everything . When I call the function file for example it will log every single line as if it was executed .
  3. I have tried creating a log function :

    logthis(){
        ## print the command to the logfile
        echo "$(date) $@" >> $historyfile
        ## run the command and redirect it's error output
        ## to the logfile
        eval "$@" 2>> $historyfile
    }
    

    This seems to work most of the time. But when I do, for example:

    case $OPTION in
        1) logthis "some_function 1 2 3" ;;  
        2) some_function 2 1 3 ;;
        3) some_function 3 1 2 ;;
    esac
    

it will not work as I will lose the arguments 1 2 3

Do you have any other ideas of doing an elegant logging system inside a bash script?

4
  • 2
    Tracing can be enabled and disabled as needed within your script using set -x and set +x; it's the finer-grained version of your option 1. Commented Sep 28, 2017 at 14:55
  • 1
    Option 2 doesn't require the -i option; history is enabled by default in an interactive shell, but does not require an interactive shell. Commented Sep 28, 2017 at 14:55
  • yes, I can enable and disable debug mode , but was searching for more elegant way instead going through all the code and setting set -x / set +x Commented Sep 29, 2017 at 11:16
  • 1
    Here is a comprehensive implementation of logging for bash: github.com/codeforester/base/blob/master/lib/stdlib.sh Commented Mar 22, 2019 at 1:05

2 Answers 2

7

Get rid of the eval in your log function. Just write "$@" to execute the passed command.

logthis() {
    echo "$(date): $@" >> "$historyfile"
    "$@" 2>> "$historyfile"
}

Then you can log a command by simply prepending logthis. No need for extra quotes.

logthis some_function 1 2 3

This will very nicely preserve all the arguments--even if they have whitespace or other special characters.

I'd recommend a minor improvement to the echo command as well. If you use printf %q it'll log arguments with whitespace better.

echo "$(date):$(printf ' %q' "$@")" >> "$historyfile"
Sign up to request clarification or add additional context in comments.

5 Comments

For the last bit , why not just use printf for the whole thing ?
printf will repeat the format string for each extra argument. Can't do that and print the date in one printf.
unfortunately when I try to pass variables for example : logthis command $1 $2 something breaks and the whiptail window doesnt display ... :/
You should write a whiptail-specific question if you continue to have problems. I'm not familiar with it.
here is a link to something I found regarding whiptail: stackoverflow.com/questions/1970180/… , so it seems that whiptail is switching fd which makes it impossible to use the logthis function in all cases .
2

Try set -v

That doesn't parse the commands like set -x, just outputs what gets executed.

set -v
: all the things 'in' $0, 'yay!'

outputs exactly : all the things 'in' $0, 'yay!'
Doesn't even parse $0.

Arguments recorded, minimal spam. ;)

Consider wrapping curlies around the main block of code to manage output logging.

{ # after setup to parse args, set vars & traps, declare funcs, etc
  your bulk code here
} 2>&1 | tee /some/file.log

You can save the set -x spam for --verbose mode. ;)

2 Comments

Thanks , but this still will log also all the whiptail menus for example. I cant output them to /dev/null like the variable declarations for example.
Can you abstract the menus to a function that turns -v off at the start and on at the end? You're asking for granular logging, so I'm afraid you're going to have to write granular code.

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.