logoalt Hacker News

amarshall • today at 1:46 PM • 9 replies • view on HN

Or you can just…not quote the tilde. Folks always seem to reflexively quote “strings” in Bash while not realizing that (almost) everything is a string and most strings are not quoted and it would be odd to do it (e.g. no one is doing `"ls" "-a" "foo"`).


Replies

kragen • today at 8:57 PM

Not quoting the tilde doesn't help:

    : tmp; echo $PATH:~
    /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:~
Edit: mjmas points out that I am wrong, because different Calvinball rules apply to variable assignments:

    : tmp; x=$PATH:~
    : tmp; echo "$x"
    /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/user
Also in general your advice is very bad advice. In command-line arguments, which is the vast majority of the code of any shell script, you should always quote strings that contain variable expansion unless you want them to be implicitly split on spaces after variable expansion, because the thing you're storing in the variable is not an atomic string but rather a space-separated list.

This is almost never what you actually want, and in the rare cases that you do want to store a list, the shell's implicit space splitting usually breaks on filenames containing spaces. Bash has actual array variables which make it possible, but horrible, to handle this in a first-class way:

    : tmp; x=("foo bar" baz)
    : tmp; echo "${x[@]}"
    foo bar baz
    : tmp; touch "${x[@]}"
    : tmp; ls -l "${x[@]}"
    -rw-r--r-- 1 user user 0 Oct 11 17:52  baz
    -rw-r--r-- 1 user user 0 Oct 11 17:52 'foo bar'
This ksh feature is absent in the Bourne shell and in dash, but MirBSD ksh, Bash, and zsh all have it.

In general, whenever you see a $variable $expansion in a shell script outside of double quotes, you should suspect that the shell script will probably fail if your filenames or directory names contain spaces. In very many cases, this results in path injection security vulnerabilities. There are contexts where unquoted $variable $expansion is safe, but they are relatively rare.

However, relevant detail here! One of those safe contexts is actually variable assignment, where as mjmas pointed out in their helpful comment below, unquoted variable expansion is actually perfectly safe:

    : tmp; a='x  y'
    : tmp; b=α:$a:ω
    : tmp; echo "$b"
    α:x  y:ω
➕ show 1 reply
Cockbrand • today at 2:54 PM

I feel like this is common knowledge and should not be worth mentioning. But then it apparently is not common knowledge, as the article proves.

Have I run into this at some point?

I certainly have.

Have I learned to quote better and only where appropriate from it?

I certainly have.

Bourne compatible shells take a while to learn and require some experience. This won't change, but alternatives exist, with their own caveats.

isityettime • today at 8:02 PM

This feature, or at least the syntactic unit in question has a name here "bare words". You also have these in some contexts in Perl and Ruby, YAML, and probably some other languages, idk.

I've also seen this even with people who seem like generally competent shell users. Idrgi

dylan604 • today at 2:06 PM

Unless you're running a shell command from python. That was the first time I saw a command string broken down into "string" arguments for every thing like that.

➕ show 1 reply
cr125rider • today at 2:02 PM

The trick is to quote explicitly and correctly. “ and ‘ are different.

➕ show 1 reply
paulddraper • today at 2:19 PM

People quote both too often and too little.

GENERAL RULE

1. Double-quote dollar sign expressions, and nothing else.

  foo

  "$bar"/foo

  baz:"$(cat example.txt)"

  exec cmd "$@"
2. Single-quote words with a literal special character, and nothing else.

  'Die Hard'

  'ke$ha'
---

I should point out that the author's example is NOT fixed by different quoting though.

  # original
  export PATH="$PATH:~/.local/bin/"

  # without unnecessary quotes
  export PATH="$PATH":~/.local/bin/
Because tilde expansion only happens at the beginning of the word.
➕ show 2 replies
vips7L • today at 2:08 PM

Or just stop using bash. It’s a terrible language to write and has tons of footguns.

➕ show 3 replies
Grimeton • today at 6:34 PM

You need to read the manual.

chasil • today at 4:10 PM

I think the appropriate method by POSIX rules would be:

  export PATH="$PATH:"~/.local/bin
I may be wrong. If I'm not, that works in any POSIX-compliant shell.

Edit: this appears to work properly with mksh on my phone:

  :/ $ export PATH="$PATH:"~/.local/bin
  :/ $ print $PATH
  /product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin:~/.local/bin
➕ show 1 reply