1

I have a script that I've simplified here:

#!/bin/bash
# test.sh

set -u

### test-script ###
cat <<SCRIPT > ~/test-file-output
#!/bin/bash
set -e

usage() {
  cat <<EOF
Does a thing
EOF
}

for opt in "$@"; do
  case $opt in
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Invalid option: $opt" >&2
      usage
      exit 1
      ;;
  esac
done
SCRIPT
chmod +x ~/test-file-output

and because the parent script has set -u it is catching the opt var in the child script in the heredoc; the child script sets opt to the value of $@.

Is there a way to get it to ignore all the vars and text in the heredoc since it's not part of the parent script itself? I thought it would treat the heredoc as a giant string and not parse it according to script syntax.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Wimateeka
  • 1,005

1 Answers1

1

A heredoc is a giant string, but if the terminator string (SCRIPT in your << SCRIPT) is not quoted, it acts like a double-quoted string, so any variables are expanded. If any part of the terminator is quoted in redirection (e.g. << "SCRIPT"), then it acts like a single-quoted string, so no variables expand.

Consider this script:

$ cat heredoc.sh 
#!/bin/bash
var=xyz
cat <<EOF
var=$var
EOF

cat <<'EOF'
var=$var
EOF

$ ./heredoc.sh 
var=xyz
var=$var

The first EOF is not quoted, so $var expands. The second is, so the string going to cat contains a literal $var.

ilkkachu
  • 138,973