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?

  • 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

  • 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