Node: path: tilde expansion

Created on 31 Jan 2015  Â·  14Comments  Â·  Source: nodejs/node

Carrying over this often-requested feature from https://github.com/joyent/node/issues/2857. There's two mechanisms in Unix concering the tilde in paths:

  1. expand ~ at the beginning of a path string to the current user's home directory.
  2. expand ~user at the beginning of a path string to the given user's home directory.

The first part should be quite trivial to add if #682 get implemented. The second part is far more uncommon and would likely require passwd parsing (for which consensus seems to be that it's libuv territory - https://github.com/libuv/libuv/issues/11), and god-knows-what on Windows.

Would we be satisified if only the first part gets added to all relevant path functions? I assume this could be added in a non-breaking way, which wouldn't require a semver-major bump.

Most helpful comment

No. You can use os.homedir() to expand the ~ yourself, or use a third party module like untildify.

All 14 comments

> var fs = require('fs');
undefined
> fs.mkdirSync('~test');
undefined
> fs.writeFileSync('~test/~file', 'myData');
undefined
> fs.readFileSync('~test/~file', 'utf8');
'myData'

Seeing as right now one can viably and reliably write to / read from / create directories with a tilde as a prefix, I'd say that this would cause a breaking change and require semver-major. However, I would favor this feature and wouldn't cry if I 2 wasn't implemented.

I'm -1 on this. On the one hand yes, many developers may expect ~ on *nix to be (internally) replaced with the path to a user's home directory, but I'm not sure Windows users would expect that since Windows does not support ~ in the command prompt and other places?

There's also other issues:

  • The tilde expansion is really a bash-ism (yes, bash is very common, but still...) and not a more general *nix shell feature so the feature is "limited" more than just to a particular platform.
  • How do you escape the tilde (in a sane way) to keep expansion from happening?
  • If we add this, where does the support for bash-isms end? Do we also support the other ~ expansions (e.g. ~+, ~-, ~1, ~2, etc)? Do we add support for inline replacing of environment variables in strings (e.g. fs.mkdirSync('$HOME/foo'))? Do we keep parity when bash adds other new features? What about other shells' features?

-1, similar reasons to @mscdex, mainly because this is a shell construct and we are not building a shell.

How do you escape the tilde (in a sane way) to keep expansion from happening?

Good point. While shells allow a simple \~, the only way I can think of in JS would be '\\~' which is beyond ugly. Along with the (very slight) possibility of a breaking change, I'm almost convinced to leave this out of core. I'll close this shortly if no serious support is obtained.

I think it's decided then.

For anyone needing tilde expansion, have a look at untildify. I'm planning to add support for the more exotic tilde expansions of bash to it too.

This functionality could be added to the posix._makeLong() function (lib/path.js), and that avoids the problem of what to do on Windows. win32._makeLong() has it's own logic for resolving path names, for POSIX there is no 'smart' logic.

has this been fixed?

it's causing aws-sdk to break, which uses ~/.aws/credentials

CredentialsError: Missing credentials in config

I just tested node 6.3.1 and it's not fixed. You can test it by doing something like require('fs').readFileSync('~/.bash_history').toString() in the REPL.

No. You can use os.homedir() to expand the ~ yourself, or use a third party module like untildify.

For everyone who stumbles upon this. Here's what I came up with (if you're working with a file in a directory / directories):

let credentials = '~/root/foo/bar/key.json';
const credParts = credentials.split(path.sep);

if (credParts[0] === '~') {
  credParts[0] = os.homedir();
  credentials = credParts.reduce((memo, part) => path.join(memo, part), '');
}

For everyone who stumbles upon this. Here's what I came up with:

Two other variations:
If your original path is OS appropriate

let credentials = '~/root/foo/bar/key.json';
credentials = credentials.replace(/^~/, os.homedir());

If you use node >= v6
```js
let credentials = '~/root/foo/bar/key.json';
const credParts = credentials.split(path.sep);

if (credParts[0] === '~') {
credParts[0] = os.homedir();
}
credentials = path.join(...credParts);

Your examples will fail on files named ~file. I'd suggest you use untildify.

Your examples will fail on files named ~file

That's valid (asking as an occasional non-expert *inx user)?

Your examples will fail on files named ~file

That's valid (asking as an occasional non-expert *inx user)?

Sure, filenames can start with a literal ~ – tilde expansion is purely a shell thing, the OS or the filesystem doesn’t care. (Also fyi, in shells ~foo typically expands to the home directory of the user named foo).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danialkhansari picture danialkhansari  Â·  3Comments

sandeepks1 picture sandeepks1  Â·  3Comments

Icemic picture Icemic  Â·  3Comments

vsemozhetbyt picture vsemozhetbyt  Â·  3Comments

seishun picture seishun  Â·  3Comments