bash script to bulk decode files
Use the -u and -e switches to bash, when possible, because they help to find bugs.  Your log decoder may return errors that you want to ignore (making -e unhelpful) but -u is safe here.

Move the echo … exit pattern into a function:

die() { printf "%s\n" "$@" &gt;&amp;2; exit 1; }


Quotes aren't needed on the left-hand side of [[ tests.  For "die unless foo" assertions, consider writing them in the form foo || die "test failed":

[[ -z $cmd ]] &amp;&amp; die "…"


(( $# == 1 || $# &gt; 2 )) is more clearly written in the affirmative, as (( $# == 0 || $# == 2 ))

Collect the file list once instead of twice.  Use globbing instead of find to get proper handling of filenames with spaces in them.  Include contributing variables in error messages.  Do a cd first so that you don't need to decompose filenames later.  (( expressions return false when zero so there's usually no need to explicitly write x == 0:

shopt -s nullglob
pushd "$log_dir" || die "can't chdir to $log_dir"
declare -a in=( *.cbllog )
popd || die "can't return to original working directory"
(( ${#in} )) || die "no cbllog files in $log_dir"
…
for path in "${in[@]}"; do


Your command detector has a bug if cbl-log is an alias or function.  Use type -P instead.  If you truncate using read instead of head, the assignment doubles as an emptyness test:

read cmd &lt; &lt;( type -P {./,}cbl-log ) || die "cbl-log should be in PATH or current directory"


Your basename implementation has a bug if log_dir contains globbing characters.  This is obsoleted by the pushd above but something like file=${path##*/} or file=$( basename "$path" ) would work.

mkdir -p is a no-op when the directory exists; no need to test for that yourself.

You'll need execute permission on $out_dir along with write.

Putting it all together:

    #!/bin/bash -u 
    die() { printf "%s\n" "$@" &gt;&amp;2; exit 1; }

    (( ${#} == 0 || ${#} == 2 )) || die "Usage:    cbldecode"  "          cbldecode &lt;source directory&gt; &lt;target directory&gt;"
    log_dir=${1:-$PWD}
    out_dir=${2:-"decoded"}
    read cmd &lt; &lt;( type -P {./,}cbl-log ) || die "cbl-log should be in PATH or current directory"

    pushd "$log_dir" || die "can't chdir to $log_dir"
    shopt -s nullglob
    declare -a in=( *.cbllog )
    popd || die "can't return to original working directory"
    (( ${#in} )) || die "no cbllog files in $log_dir"

    mkdir -p "$out_dir"
    [[ -w $out_dir &amp;&amp; -x $out_dir ]] || die "insufficient permissions on $out_dir"

    for basename in "${in[@]}"; do 
        "$cmd" logcat "$log_dir/$basename" "$out_dir/${basename%.cbllog}"
    done


For faster conversion, add &amp; to the cbl-log invocation, or adapt the script to send basenames to xargs -P.