Bash HereDoc variable Error on MacBook with newline characters

51 views Asked by At

I have a script that happily runs on Ubuntu 22.04 with Bash. However, the same script starts giving errors when executed under bash (v 5.2) on an Arm MacBook.

Below is the problematic piece of the script:

my_script=$(cat <<EOF
while getopts \":lr:e:\" option; do \n
  case \\\${option} in \n
     l) \n
        cat file.txt \n
        exit;; \n
     e) \n
        vi file.txt \n
        exit;; \n
     r)  \nB
         rm file.txt \n
        exit;; \n
\n
     *) \n
       echo \"invalid option. \" \n
     exit;; \n
  esac \n
done \n

EOF
)

The plan is to dump this variable into a file as a standalone script so it should preserve the formatting however, I am running into below error:

syntax error near unexpected token `;;'

Can someone please suggest why bash is complaining for this ? This is how I am writing the script to a file: printf \"${my_script}\"> my_script_file

1

There are 1 answers

0
Charles Duffy On

The approach you're taking is so error-prone that going into exactly how it fails is dependent on details I don't think it would be worthwhile to delve.

There are several better ways to do this.


Use A Function

This has the distinct and significant advantage that static-checking tools can validate your syntax when checking the parent script, vs needing to use them to open the generated child script.

# define the function we want to write in our existing local shell
my_script() {
  while getopts ":lr:e:" option; do
    case ${option} in
       l) cat file.txt; exit;;
       e) vi file.txt; exit;;
       r) rm file.txt; exit;;
       *) echo "invalid option" >&2; exit 1;;
    esac
  done
}

# write shebang, function text, and a function invocation to a file
printf '%s\n' \
  '#!/usr/bin/env bash' \
  "$(declare -f my_script)" \
  'my_script "$@"' \
  >file

Use A Quoted Heredoc

Using <<'EOF' instead of <<EOF tells the shell not to try to run any expansions, but instead to keep your heredoc content exactly as previously defined.

Combining this with storing an exact copy of that heredoc in your output file (which you get with cat, or if it's in a variable, printf '%s\n' "$variable", but not with echo "$variable" or printf "$variable") and the need to worry about how unescaping occurs is mooted.

cat >file <<'EOF'
#!/usr/bin/env bash
while getopts ":lr:e:" option; do
  case ${option} in
     l) cat file.txt; exit;;
     e) vi file.txt; exit;;
     r) rm file.txt; exit;;
     *) echo "invalid option" >&2; exit 1;;
  esac
done
EOF