0

I am trying to make two functions in bash. One function that loops through a directories' contents and makes an md5 hash for each file storing them in an .md5 checksum file in the same directory. The other function to check that checksum file against it's directory contents. I am trying to keep the .md5 file standard which I believe is the md5 hash followed by the file name on one line for each file.

Requirements: I can only use the digest command for creating the hash; which errors on directories...

Ideally I can assume that the inbound files to archive are n number of non directory items. Nevertheless I want to be able to check the digest first for errors. Even if it fails for some unknown reason I can make another attempt before exiting the entire script with errors, or at least logging the inability to make a digest for that particular file and continuing.

Main Question: How can I if condition error check a command and also store the output in a variable?

Update: Solution Here

if ! digest -a md5 "$file" ; then ; fi

I attempted other variants such as piping to a temp file getting the variable and then removing the temp file, but that felt excessive for this task. I also looked into accessing the exit code using $? but based on what I have read in the past I am unsure of this communities solidarity on its use, let me know what you think.

Secondary Question: Is there a better way to achieve this?

Below are my current incomplete functions. Thanks!

function make_directory_md5 () {
    local md5_file=$"$1/$(basename $1).md5"
    local attempts=$(($2 - 1))
    if [ ! -f "$md5_file" ] ; then
        if ! touch "$md5_file" >> "$file_log" ; then
            warning_handle "Could not make checksum file $md5_file, continuing anyway..."
            if [ "$verbose_mode" = true ]; then verbose_handle "Checksum file $md5_file process failed." ; fi
        else
            if [ "$verbose_mode" = true ]; then verbose_handle "Checksum file $md5_file created..." ; fi
            #Loop through file contents and get md5 and filename and output to checksum file
            for file in "$1/*" ; do
                if [ ! -d "$file" ] ; then
                    if [ ! "$file" = "$md5_file" ] ; then #Skip md5 file.
                        if ! hash="$(digest -a md5 $file)" >> "$file_log"  ; then
                            warning_handle "Problem occured when attempting to digest $file"
                            if [ "$verbose_mode" = true ]; then verbose_handle "Attempting to try again, attempts left: $attempts" ; fi
                            if [ $attempts -ge 1 ]; then
                                make_directory_md5 $1 $attempts
                            else
                                warning_handle "Attempt to create checksum file $md5_file FAILED out of attempts."
                                local sucess=false
                                if ! rm "$md5_file" >> "$file_log" ; then
                                    if [ -f "$md5_file" ] ; then
                                        warning_handle "Could not remove $md5_file and it still exists!"
                                    fi
                                else
                                    if [ "$verbose_mode" = true ]; then verbose_handle "Removed $md5_file" ; fi
                                fi
                            fi
                        else
                            echo "$hash $file" >> "$md5_file"
                        fi
                    fi
                else
                    warning_handle "Cannot hash directory! Skipping $file"
                    #Recursive decent into new directory. Or assume no directories ever...
                fi
            done
        fi
    else
        warning_handle "Attempted to create checksum file $md5_file but it already exists..."
        if [ "$verbose_mode" = true ]; then verbose_handle "Attempting to remove $md5_file..." ; fi
        if ! rm "$md5_file" >> "$file_log" ; then
            if [ -f "$md5_file" ] ; then
                warning_handle "Could not remove $md5_file and it still exists. Continuing with backup..."
                if [ "$verbose_mode" = true ]; then verbose_handle "Checksum file $md5_file process failed." ; fi
            else
                if [ "$verbose_mode" = true ]; then verbose_handle "Old checksum file $md5_file removed..." ; fi
                if [ "$verbose_mode" = true ]; then verbose_handle "Attempting to try again, attempts left: $attempts" ; fi
                if [ $attempts -ge 1 ] ; then
                    make_directory_md5 $1 $attempts
                else
                    local sucess=false
                    warning_handle "Attempt to create checksum file $md5_file FAILED out of attempts."
                    if [ "$verbose_mode" = true ]; then verbose_handle "Checksum file $md5_file process failed." ; fi
                fi
            fi
        else
            if [ "$verbose_mode" = true ]; then verbose_handle "Old checksum file $md5_file removed..." ; fi
            if [ "$verbose_mode" = true ]; then verbose_handle "Attempting to try again, attempts left: $attempts" ; fi
            if [ $attempts -ge 1 ] ; then
                make_directory_md5 $1 $attempts
            else
                local sucess=false
                warning_handle "Attempt to create checksum file $md5_file FAILED out of attempts."
                if [ "$verbose_mode" = true ]; then verbose_handle "Checksum file $md5_file process failed." ; fi
            fi
        fi
    fi
}

function check_directory_md5 () {
    local md5_file=$"$1/$1.md5"
    for file in "$1"/. ; do
        echo "$file"
    done
}

make_directory_md5 "/home/jsharpe/out" 3
  • For your first question, if ! OUTPUT="$(some commands here)"; then foo; fi seems to work. – DopeGhoti Nov 20 '15 at 18:42
  • 1
    The md5sum command might already do what you need, with the -c flag. – user3188445 Nov 20 '15 at 18:51
  • @DopeGhoti I updated my script to add what you have suggested which does store the output to a variable but the if condition does not seem to catch when there is an error. – JaredTS486 Nov 23 '15 at 16:28
  • @user3188445 I am unable to use md5sum in this case. – JaredTS486 Nov 23 '15 at 16:29
  • That's odd, @JaredTS486; when I try if ! FOO=$(/bin/true); then echo "then"; else echo "else"; fi, I get else returned as expected; and I get then if I try if rather than if !. Using /bin/false also works as expected for me. – DopeGhoti Nov 23 '15 at 17:54
  • @DopeGhoti I believe you are correct. When I ran the script for testing I assumed the echo was the only output that would occur but the command itself seemed to be outputting errors. I noticed after piping my echo to a file I still saw errors in the shell. I needed to separate directory errors and file permission errors anyway so I if conditioned my loop to catch directories. But your suggestion catches file read errors just fine. Thanks! – JaredTS486 Nov 23 '15 at 18:30
  • If you want to suppress error messages, or capture them, you can always 2> /dev/null or 2> /path/to/error.log. – DopeGhoti Nov 23 '15 at 18:33
  • @DopeGhoti Thanks, I can push the errors digest makes to my log file like so if ! hash="$(digest -a md5 $file 2>> "$file_log")" my log file looks nice and my .md5 file appears to be working correctly. Now on to verifying. – JaredTS486 Nov 23 '15 at 19:41

0 Answers0