3

If I do this:

ls ~/Dev/Project/Assets/_Core/

I get a super duper directory listing! Yay! But if I do this:

assetsPath=$(head -n 1 .config | perl -ne 'print if s/^assets=(.*)/\1/g')
echo $assetsPath
ls $assetsPath

I get:

~/Dev/Project/Assets/_Core/ # this was the variable value from the echo
ls: ~/Dev/Project/Assets/_Core/: No such file or directory

I even tried using ${assetsPath} but that didn't work either?

brandonscript
  • 68,675
  • 32
  • 163
  • 220
  • 9
    `~` expansion happens before variable expansion. You can't stick it in a variable. Use `$HOME` or don't put it in the variable. – Etan Reisner Mar 27 '15 at 20:28
  • Ah! I should be able to replace the tilde in the variable before calling the ls yes? – brandonscript Mar 27 '15 at 20:31
  • Not sure I understand what you mean. You need to avoid having `~` in a variable if you want the shell to expand it. `~` is a special and specific shell expansion. See the output from `echo ~root:~root` for example as compared to `echo $HOME:$HOME` for example. `~` should really just be avoided in general is my feeling. – Etan Reisner Mar 27 '15 at 20:35
  • Well, I'd like to support it in the config but I can easily just replace ~ in the string with $HOME – brandonscript Mar 27 '15 at 20:37
  • You really don't want to. It isn't equivalent. It is a shell thing (it does more than just expand to `$HOME` also). Just make them write the value out (or if you expand shell variables in there already make them use `$HOME`). – Etan Reisner Mar 27 '15 at 20:52
  • 1
    @remus This solution is not legitimate unless you add `if [[ $assetsPath == *[^/.~a-zA-Z0-9]* ]]; then echo "preventing bad things from happening!!"; exit 1; fi` before the eval. – that other guy Mar 27 '15 at 21:12
  • 1
    Yeah, `eval` is only a "solution" if you include "adds a giant security hole" in your requirements (or do careful validation of the value you pass to it as @thatotherguy suggested though I can't, offhand, evaluate whether that validation is enough). – Etan Reisner Mar 27 '15 at 21:15

1 Answers1

3

As a partial solution:

assetsPath=${assetsPath//'~'/$HOME}

What this doesn't address is ~username expansion; if your assetsPath uses this, then you need a bit more logic (which I think I've added in a separate StackOverflow answer; looking for the question).


It also doesn't address ~ in non-leading position, where it shouldn't be expanded. To handle both corner cases, I'm going to self-plagarize a bit:

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

...then:

assetsPath=$(expandPath "$assetsPath")
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thank you. Much better. I don't need to support the `~username` expansion, so this will do. – brandonscript Mar 27 '15 at 21:19
  • Ahh -- found the place where I went to the effort to do it right: http://stackoverflow.com/a/18742860/14122; copied that into the other question at hand, as http://stackoverflow.com/a/29310477/14122 – Charles Duffy Mar 27 '15 at 21:24
  • @EtanReisner, ...one of those two is deleted over accuracy concerns, no? – Charles Duffy Mar 27 '15 at 21:27
  • @EtanReisner, eh -- I can't leave well enough on this one; putting some more effort into it. – Charles Duffy Mar 27 '15 at 22:20
  • @EtanReisner, I believe I've addressed the issues you mention. – Charles Duffy Mar 27 '15 at 22:30
  • 1
    Turns out I couldn't leave well enough alone either... so I did this: https://github.com/deryni/expandTilde.sh – Etan Reisner Oct 19 '15 at 02:20
  • the `read` will go screwy on backslashes, but i guess `getent` doesn't do those? – mikeserv Dec 13 '15 at 04:16
  • Backslashes can actually be used inside `getent` output when there's a literal `:`, so the lack of a `-r` argument to `read` here (which would turn off its usually-undesired backslash processing) is intentional; it ensures that IFS characters that are intended to be literal rather than field separators are parsed as such. – Charles Duffy Dec 13 '15 at 06:28
  • Is it possible to get a colon in the `getent` output from the `passwd` file or only from other sources of data (when configured appropriately)? – Etan Reisner Mar 16 '16 at 12:45