Fix shellcheck findings (#30)

Also add rules_shellcheck to avoid regressions.

Fixes #28
diff --git a/MODULE.bazel b/MODULE.bazel
index 294f5f7..cbb5d37 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -11,3 +11,5 @@
 use_repo(sh_configure, "local_config_shell")
 
 register_toolchains("@local_config_shell//:all")
+
+bazel_dep(name = "rules_shellcheck", version = "0.3.3", dev_dependency = True)
diff --git a/shell/runfiles/BUILD b/shell/runfiles/BUILD
index fb86e50..63e6fee 100644
--- a/shell/runfiles/BUILD
+++ b/shell/runfiles/BUILD
@@ -20,3 +20,8 @@
     },
     tags = ["manual"],
 )
+
+exports_files(
+    ["runfiles.bash"],
+    visibility = ["//tests/runfiles:__pkg__"],
+)
diff --git a/shell/runfiles/runfiles.bash b/shell/runfiles/runfiles.bash
index 8e1f944..7f9b6d7 100644
--- a/shell/runfiles/runfiles.bash
+++ b/shell/runfiles/runfiles.bash
@@ -95,7 +95,7 @@
   fi
 fi
 
-case "$(uname -s | tr [:upper:] [:lower:])" in
+case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
 msys*|mingw*|cygwin*)
   # matches an absolute Windows path
   export _RLOCATION_ISABS_PATTERN="^[a-zA-Z]:[/\\]"
@@ -226,6 +226,7 @@
 #
 # Note: This function only works correctly with Bzlmod enabled. Without Bzlmod,
 # its return value is ignored if passed to rlocation.
+# shellcheck disable=SC2001 # patterns are either too complex or contain slashes
 function runfiles_current_repository() {
   local -r idx=${1:-1}
   local -r raw_caller_path="${BASH_SOURCE[$idx]}"
@@ -234,7 +235,7 @@
   if [[ "$raw_caller_path" =~ $_RLOCATION_ISABS_PATTERN ]]; then
     local -r caller_path="$raw_caller_path"
   else
-    local -r caller_path="$(cd $(dirname "$raw_caller_path"); pwd)/$(basename "$raw_caller_path")"
+    local -r caller_path="$(cd "$(dirname "$raw_caller_path")" || return 1; pwd)/$(basename "$raw_caller_path")"
   fi
   if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
     echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): caller's path is ($caller_path)"
@@ -361,7 +362,8 @@
     # with a space and spaces, newlines, and backslashes have to be escaped as
     # \s, \n, and \b.
     if [[ "$1" == *" "* || "$1" == *$'\n'* ]]; then
-      local search_prefix=" $(echo -n "$1" | sed 's/\\/\\b/g; s/ /\\s/g')"
+      local search_prefix
+      search_prefix=" $(echo -n "$1" | sed 's/\\/\\b/g; s/ /\\s/g')"
       search_prefix="${search_prefix//$'\n'/\\n}"
       local escaped=true
       if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
@@ -372,10 +374,12 @@
       local escaped=false
     fi
     # The extra space below is added because cut counts from 1.
-    local trim_length=$(echo -n "$search_prefix  " | wc -c | tr -d ' ')
+    local trim_length
+    trim_length=$(echo -n "$search_prefix  " | wc -c | tr -d ' ')
     # Escape the search prefix for use in the grep regex below *after*
     # determining the trim length.
-    local result=$(__runfiles_maybe_grep -m1 "^$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b "${trim_length}-")
+    local result
+    result=$(__runfiles_maybe_grep -m1 "^$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b "${trim_length}-")
     if [[ -z "$result" ]]; then
       # If path references a runfile that lies under a directory that itself
       # is a runfile, then only the directory is listed in the manifest. Look
@@ -404,7 +408,7 @@
         fi
         # The extra space below is added because cut counts from 1.
         trim_length=$(echo -n "$search_prefix  " | wc -c)
-        prefix_result=$(__runfiles_maybe_grep -m1 "$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b ${trim_length}-)
+        prefix_result=$(__runfiles_maybe_grep -m1 "$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b "${trim_length}"-)
         if [[ "$escaped" = true ]]; then
           prefix_result="${prefix_result//\\n/$'\n'}"
           prefix_result="${prefix_result//\\b/\\}"
@@ -465,4 +469,5 @@
 }
 export -f runfiles_rlocation_checked
 
-export RUNFILES_REPO_MAPPING=$(runfiles_rlocation_checked _repo_mapping 2> /dev/null)
+RUNFILES_REPO_MAPPING=$(runfiles_rlocation_checked _repo_mapping 2> /dev/null)
+export RUNFILES_REPO_MAPPING
diff --git a/tests/runfiles/BUILD b/tests/runfiles/BUILD
index dce2922..52d9867 100644
--- a/tests/runfiles/BUILD
+++ b/tests/runfiles/BUILD
@@ -1,3 +1,4 @@
+load("@rules_shellcheck//:def.bzl", "shellcheck_test")
 load("//shell:sh_test.bzl", "sh_test")
 
 sh_test(
@@ -5,3 +6,10 @@
     srcs = ["runfiles_test.bash"],
     deps = ["//shell/runfiles"],
 )
+
+shellcheck_test(
+    name = "runfiles_shellcheck_test",
+    data = ["//shell/runfiles:runfiles.bash"],
+    format = "gcc",
+    severity = "warning",
+)