0

When I input a relative directory path name using the 'read' command in Bash, it is not recognized as a valid directory by the -d test:

Relative directory path ~/tmp fails the if/-d test:

corba@samwise:~$ read CMDLINE_FILENAME
~/tmp
corba@samwise:~$ echo "$CMDLINE_FILENAME"
~/tmp
corba@samwise:~$ if [ -d $CMDLINE_FILENAME ]; then echo "Valid directory!"; fi
corba@samwise:~$

Relative directory path ../tmp fails the if/-d test:

corba@samwise:bin$ read CMDLINE_FILENAME
../tmp
corba@samwise:bin$ echo "$CMDLINE_FILENAME"
../tmp
corba@samwise:bin$ if [ -d $CMDLINE_FILENAME ]; then echo "Valid directory!"; fi
corba@samwise:~$

But an absolute directory path succeeds:

corba@samwise:~$ read CMDLINE_FILENAME
/home/corba/tmp
corba@samwise:~$ echo "$CMDLINE_FILENAME"
/home/corba/tmp
corba@samwise:~$ if [ -d $CMDLINE_FILENAME ]; then echo "Valid directory!"; fi
Valid directory!
corba@samwise:~$

I expected if [ -d ~/tmp ] and if [ -d ../tmp ] to be recognized as valid directory names. They were not.

I tried the solution that was offered in How to manually expand a special variable (ex: ~ tilde) in bash, but it only works for tilde and not for other relative paths like ../, ../../, or ./

I tried the variations of quoting/double-quoting and single/double square brackets in the if statement and get the same error in all cases. And these errors occur on MSYS2 (Git Bash) and Ubuntu 22.

CorbaTheGeek
  • 71
  • 1
  • 4
  • 1
    The later two works just fine for me on Debian Linux. Maybe it's a msys2 thing? Have you tried `[[ -d ~/tmp ]]` and `[ -d ../tmp ]]`? – Allan Wind Dec 03 '22 at 07:47
  • 2
    Does this answer your question? [How to manually expand a special variable (ex: ~ tilde) in bash](https://stackoverflow.com/questions/3963716/how-to-manually-expand-a-special-variable-ex-tilde-in-bash) – Joe Dec 03 '22 at 09:48
  • @Joe Thanks Joe. I had tried that approach with a string substitution `${CMDLINE_FILENAME/~\//$HOME/}`, but realized that it only works for tilde and not the other relative path name approaches like `../tmp`, `../../tmp` or `./`. – CorbaTheGeek Dec 03 '22 at 18:11

1 Answers1

2

~/tmp actually is an absolute path from the bash point of view. However this relies on bash to substitute the ~ to the user accounts home folder path, so to something like /home/users/someone/tmp which clearly is an absolute path. That is a buildin feature of the bash shell usually called "path expansion". A relative path would be something like ./tmp or just tmp, so something that has to be interpreted relative to the current working directory of the process.

That substitution does not get applied here apparently. You can use a slightly altered command to achieve what you are looking for by explicitly forcing such expansion in a sub shell command:

if [ -d `eval echo $CMDLINE_FILENAME` ]; then echo "Valid directory!"; fi

That one works for absolute and relative paths, but also for entries that rely on the bash path expansion:

bash:~$ read CMDLINE_FILENAME 
~/tmp
bash:~$ echo "$CMDLINE_FILENAME"
~/tmp
bash:~$ if [ -d `eval echo $CMDLINE_FILENAME`]; then echo "Valid directory!"; fi 
Valid directory!

This does carry the risk of miss usage, though: the eval directive is a pretty mighty tool ...

arkascha
  • 41,620
  • 7
  • 58
  • 90
  • 1
    The danger with this approach is that someone could enter something like "rm -rf *" into the command line read, which is then evaluated to disastrous results. Is there a less powerful way to convert these temporary paths into absolute paths? – CorbaTheGeek Dec 03 '22 at 17:32
  • It helps to distinguish between strings and paths. The string `~/tmp`, if subjected to tilde expansion, would produce `/home/someone/tmp`, which is an absolute path. But if the string does *not* undergo tilde expansion (as is the case here, because parameter expansions are not subject to tilde expansion), then the path `~/tmp` is a relative path, referring to a directory named `~` in the current working directory. – chepner Dec 03 '22 at 17:56
  • 2
    `eval` should not be used here. Basically, if you are getting input with `read`, the user should not expect `~` to have any special meaning. If you document that `read -e` (get a line using Readline) is being used, then the user can perform tilde expansion themselves, for example by using M-& to perform tilde expansion on their input before hitting Enter. – chepner Dec 03 '22 at 18:00
  • @chepner: Sorry...I'm not familiar with that. What is the `M-&` command? – CorbaTheGeek Dec 03 '22 at 18:07
  • 1
    `M` stands for the meta key (which will probably be either the Alt key or the Escape key, depending on your terminal emulator and/or OS). Typing that key sequence while there is text in the Readline buffer (i.e., you've typed something but haven't hit Enter yet) will immediately expand any tildes just like the shell would. For example, if you've typed `~foo/bar` (and `foo` is a user on your system), the buffer will immediately change to `/home/foo/bar` (assuming `/home/foo` is `foo`'s home directory), just as if that's what you had typed in the first place. – chepner Dec 03 '22 at 18:09
  • @chepner I was actually looking for something that could be executed **inside** a Bash script. Using the Meta-& approach assumes that the script users know (or remember) that they need to convert a relative path to an absolute path before pressing Enter. To a user like me, `~/tmp` is the same as `/home/corba/tmp`, just a shorter version of the same thing. I type it without even thinking absolute or relative. – CorbaTheGeek Dec 03 '22 at 19:03
  • 1
    "`~/tmp` is the same as `/home/corba/tmp`". That's a misconception you should overcome. Tilde expansion is a shell feature, not part of the file system. – chepner Dec 03 '22 at 20:22
  • 2
    I wonder why we all have this discussion. The answer is obvious, it has been given: the expansion is not getting applied. So there are two alternatives here: 1. what you, @CorbaTheGeek are trying to implement is an _internal_ tool, something used by only you, in which case you are concerned about the feature, not security, so you can use the `eval` sproach. Or you want to implement something for general usage, then you need to consider security aspects, then you can _not_ use the convenience of path expansion. That is all there is. – arkascha Dec 03 '22 at 20:59