2

I am writing a generic script that can get passed both absolute and relative (to the current working dir) paths to existing files.

I need a fail-safe way to turn all those passed paths into absolute paths because I need to pass the paths to the "open" command, which in won't pass the current directory to the launched app.

One idea I had is to check if the path starts with a "/", and if not, then I'd prepend the current working dir's path to it.

However, I wonder if there's a smarter solution to this. Also, as I'm quite inexperienced with shell scripting and related tools, I don't even know how to test for a variable's value to start with "/". I'm looking for a portable solution to run on multiple Mac, so installing extra tools or configuration would be less than ideal.

Can someone suggest a standard script using only shipping tools that takes a variable (let's call it $path) and turns it into a absolute path if it's not already absolute?

5
  • What language(s) do you prefer for your script? Perhaps that script has a function to call for handling path expansion.
    – bmike
    Commented Feb 14, 2013 at 16:22
  • A simple "sh" script is fine, and it should only use features that are available on any recent OS X, i.e. installing extra tools is not an option. Basically, the scripts gets the file ref passed as an argument ($1) and I want to turn it into an abs path I can pass to the "open" command. Commented Feb 14, 2013 at 17:16
  • I like @Lauri Ranta's take on rolling your own solution. It's quite powerful.
    – bmike
    Commented Feb 14, 2013 at 17:18
  • Are you sure that open does not keep the calling processes current directory - man open - the examples explicitly say current working directory. (if not I think Apple needs a bug report)
    – mmmmmm
    Commented Feb 14, 2013 at 18:40
  • @Mark, it's my own app that is launched by the open cmd and then tries to open the passed files, so it may also be that I did something wrong there. Commented Feb 15, 2013 at 18:27

3 Answers 3

2

One option would be to install coreutils and use greadlink -f. It resolves symlinks and it works with /Foo/ or ~/foo.txt if they don't exist, but not with /Foo/foo.txt if /Foo/ doesn't exist.

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

This doesn't resolve symlinks, and it doesn't work with /Foo/foo.txt either.

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath /Foo/foo.txt # doesn't work
abspath /Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store

dirs -l performs tilde expansion. dirs +0 prints only the top directory if there are other directories in the stack. You could also replace the subshells with something like old="$PWD" ... cd "$old".

Ruby's expand_path works with all paths that don't exist, but it doesn't resolve symlinks.

$ ruby -e 'print File.expand_path ARGV[0]' ~/aa/bb
/Users/lauri/aa/bb$ 
1
  • I saw your post already on the older question that bmike linked to. I've now taken mschrag's version, though, and that satisfies my needs well. Commented Feb 14, 2013 at 17:13
1

I'm not sure if this answer will be comprehensive enough for your needs, but this is what I would do…

--DISCLAIMER: I haven't tested this in SH/BASH/Anything so just use this as a framework please. Feel free to edit the question after you've got it working!--

You can test if the supplied script argument begins with a character using something like if [[ $a == "/*" ]].

Since all relative paths carry with them the assumption that the working path is the parent directory, if you know the path is not already absolute you can use pwd, assuming the script or parent process hasn't changed the working directory.

If the issue of the working directory changing is significant, you'll have to use a function like find.


Some additional notes that might get you on the right path to solving your issue: https://unix.stackexchange.com/questions/6435/how-to-check-if-pwd-is-a-subdirectory-of-a-given-path

http://www.unix.com/shell-programming-scripting/36200-getting-full-path-relative-path.html

https://stackoverflow.com/questions/2172352/in-bash-how-can-i-check-if-a-string-begins-with-some-value

Hope this puts you in the right direction!

2
  • Yes, thanks for the test for the leading slash. That'll be enough for me to make my own script working. I only now realize that I was asking the question in a more general way, and so I'll see if someone come up with a solution that covers all cases before I mark the best answer. Commented Feb 14, 2013 at 16:21
  • Don't use quotes around /* in the pattern match -- within quotes, it treats the asterisk literally rather than as a match-all wildcard. Commented Feb 14, 2013 at 17:16
1

No - a path is just a string and you have to parse it to decide if it's absolute or relative to $PWD or ~

See these for more context in case my answer is too short.

2
  • Okay, I forgot about the cases where the path could start with "~". In my case, I know I won't run into this case, but for the sake of a helpful general answer I'll keep waiting for someone who has a overall solution. Supposedly, the "realpath" command can do that, which is available on Linux, it appears, but not on OSX. Commented Feb 14, 2013 at 16:18
  • Both realpath and readlink do not handle path expansion on OS X. Unless you find a package in homebrew or some other open source package to compile and call, you'll want to handle it in your shell script or do your processing on Linux.
    – bmike
    Commented Feb 14, 2013 at 16:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .