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?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
zlobul
  • 335
  • 1
  • 5
  • 20
  • 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. – chepner Sep 28 '17 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. – chepner Sep 28 '17 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 – zlobul Sep 29 '17 at 11:16
  • 1
    Here is a comprehensive implementation of logging for bash: https://github.com/codeforester/base/blob/master/lib/stdlib.sh – codeforester Mar 22 '19 at 01:05

2 Answers2

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"
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • For the last bit , why not just use printf for the whole thing ? – Mad Physicist Sep 28 '17 at 16:42
  • `printf` will repeat the format string for each extra argument. Can't do that and print the date in one `printf`. – John Kugelman Sep 28 '17 at 16:57
  • unfortunately when I try to pass variables for example : logthis command $1 $2 something breaks and the whiptail window doesnt display ... :/ – zlobul Sep 29 '17 at 12:38
  • You should write a whiptail-specific question if you continue to have problems. I'm not familiar with it. – John Kugelman Sep 29 '17 at 12:56
  • here is a link to something I found regarding whiptail: https://stackoverflow.com/questions/1970180/whiptail-how-to-redirect-output-to-environment-variable , so it seems that whiptail is switching fd which makes it impossible to use the logthis function in all cases . – zlobul Oct 02 '17 at 10:35
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. ;)

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
  • 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. – zlobul Sep 29 '17 at 07:42
  • 1
    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. – Paul Hodges Sep 29 '17 at 17:23