Values in variables and lines in script are subject to GitLab's own variable expansion before the shell sees them. A literal dollar sign intended for the shell, an awk program, or a password must be written as $$ so the runner emits a single $.
This breaks awk one-liners such as awk '{print $1}', where $1 is replaced by the empty string because no CI variable named 1 exists, and the command silently prints nothing rather than failing. It also breaks bcrypt hashes and any string containing ${.
GitLab expands variables in variables, script, before_script, after_script, rules:if, and several other keys, with slightly different rules for which are expanded by the runner versus by GitLab itself before the job is dispatched. Values that must never be expanded are better passed as masked CI/CD variables set in project settings rather than written into the YAML. Note that masking requires the value to meet length and character requirements, and GitLab silently declines to mask a value that does not, so a rejected mask leaves the secret visible in job logs.