1

I have the following code which works:

for file in $(find $1 -maxdepth 10000 -xdev -ignore_readdir_race); do
    if [[ "$file" =~ ^($OP0|$OP1|$OP2|$OP3|$OP4|$OP5|$OP6|$OP7|$OP8|$OP9)$ ]]; then (( SkipCnt++ )) # Count of skipped files
        elif [[ ! -e "$file" ]] ; then (( StalCnt++ ))                      # Count of files that existed last run, but don't now
        elif [[ ! -s "$file" ]] ; then (( ZeroCnt++ ))                      # Count of zero sized files
        elif [[ -d "$file" ]] ; then (( DirCnt++ ))                         # Count of directories
        elif [[ -h "$file" || -L "$file" ]] ; then (( LinkCnt++ ))          # Count of symbolic links
        elif [[ -c "$file" ]] ; then (( CdevCnt++ ))                        # Count of character devices
        elif [[ -b "$file" ]] ; then (( BdevCnt++ ))                        # Count of block devices
        elif [[ -p "$file" ]] ; then (( PipeCnt++ ))                        # Count of pipes
        elif [[ -S "$file" ]] ; then (( SockCnt++ ))                        # Count of sockets
        elif [[ -f "$file" && -s "$file" ]] ; then                          # File must exist, and not be any of the above.

# You can use any one of these three, listed fastest to slowest
            tar -cS --no-recursion --warning=none "$file" &>/dev/null
            # cp --preserve=all --reflink=never "$file" /dev/null
            # cat "$file" 1>/dev/null

            (( FileCnt++ ))                                                 # Count of files cache-loaded
        else
            (( SkipCnt++ ))                                                 # Count of any files not otherwise processed.
    fi

I'm attempting to convert it to a case statement, but I can't figure it out... what I've got so far (I just copied each if statement to the case statement, but it keeps throwing an error):

The error:

/usr/local/bin/cachewarmup: line 43: syntax error near unexpected token `"$file"'
/usr/local/bin/cachewarmup: line 43: `          [[ "$file" =~ ^($OP0|$OP1|$OP2|$OP3|$OP4|$OP5|$OP6|$OP7|$OP8|$OP9)$ ]]) (( SkipCnt++ ));;'

The (non-working) code:

for file in $(find $1 -maxdepth 10000 -xdev -ignore_readdir_race); do
    case true in
            [[ "$file" =~ ^($OP0|$OP1|$OP2|$OP3|$OP4|$OP5|$OP6|$OP7|$OP8|$OP9)$ ]]) (( SkipCnt++ ));;
            [[ ! -e "$file" ]]) (( StalCnt++ ));;
            [[ ! -s "$file" ]]) (( ZeroCnt++ ));;
            [[ -d "$file" ]]) (( DirCnt++ ));;
            [[ -h "$file" || -L "$file" ]]) (( LinkCnt++ ));;
            [[ -c "$file" ]]) (( CdevCnt++ ));;
            [[ -b "$file" ]]) (( BdevCnt++));;
            [[ -p "$file" ]]) (( PipeCnt++));;
            [[ -S "$file" ]]) (( SockCnt++ ));;
            [[ -f "$file" && -s "$file" ]]) tar -cS --no-recursion --warning=none "$file" &>/dev/null; (( FileCnt++ ));;
            *) (( SkipCnt++ ));;
    esac

Any ideas on what I'm doing wrong?

PyNewbie
  • 11
  • 3
  • `case` is the wrong thing to use here. `case` is for comparing a single string (emphasis: *string*, not boolean value, bash doesn't have booleans) to a series of patterns (or specific values), not for testing general conditions. – Gordon Davisson Feb 27 '23 at 18:13

1 Answers1

1

Your case of if and case

case something in interprets something as a string, not as a command. It makes little sense (if any at all) for something to be a fixed string like true.

Usually a variable is used there. It provides a non-fixed string that gets compared to pattern(s) present later in code.

In the logic of your original code there is no such variable. Your tests are commands (well, [[ is a shell keyword, not strictly a command; but it's still something that returns exit status like a command, totally not a string). If all the commands tried to compare the value of a certain variable to some patterns then probably they could be converted to case … esac. Among your commands the one with =~ does this, but the rest do something else. This does not really qualify to be converted to case … esac.

In general even tests with =~ may or may not qualify to be converted to case … esac. The problem is =~ uses a regex, while patterns for case are not regular expressions; in general it may be impossible to convert one scheme to the other strictly.

Stick to if … elif … fi, do not try to convert to syntax that does not fit.


Broader view on if and case

if depends on the exit status of some shell code that returns exit status. It may be a command (like grep or [) or something else (like [[ or a pipeline) that returns exit status like a command would. Basically after if (or elif) you can put arbitrary shell code as a condition.

case directly compares strings. It does not rely on the exit status of anything. While in principle you can convert string comparison (case) to shell code that implements the same string comparison (for if), converting arbitrary shell code (if) to string comparison (case) is cumbersome (if possible at all).


Side notes

Kamil Maciorowski
  • 69,815
  • 22
  • 136
  • 202
  • Don't all of the 'if elif' statements resolve to either true or false? That's sort of the point of 'if', isn't it? And the code isn't just counting files, it's copying certain files to /dev/null to warm up the ZFS cache... how do I do that with the suggested find "$1" -exec some command {} \;? – PyNewbie Feb 27 '23 at 06:25
  • @PyNewbie You can interpret `if` this way. The point is you cannot interpret `case` this way. – Kamil Maciorowski Feb 27 '23 at 06:28
  • @PyNewbie "how do I do that with the suggested `find "$1" -exec …`?" – This is a separate question. Please take our short [tour] to see how the site is designed to work. We won't fix your entire script in one thread. If you can divide your problems with the script to a set of well-defined questions then possibly we can fix it eventually over multiple posts. Here the question is about `if` to `case` conversion specifically. My side notes are just side notes to point you in the right direction. If you want help beyond `if` to `case` conversion then you should ask new question(s). – Kamil Maciorowski Feb 27 '23 at 06:33
  • @PyNewbie "Don't all of the 'if elif' statements resolve to either true or false?" – It really depends on what you mean by "true" and "false". In a shell the string `true` is nothing special. *If* it's interpreted and executed as a command then it returns exit status `0`. `false` as a command returns exit status `1`. The exit status of *some* command is what matters to `if`, so `if true; then` is valid (but not really useful). Instead of `true` there can be any other command-like entity, e.g. `[[ … ]]`. OTOH `case` directly compares strings, it does not rely on exit status of anything. – Kamil Maciorowski Feb 27 '23 at 06:50