Pablo Retyk
Pablo Retyk

Reputation: 5750

How to resolve a path that includes an environment variable, in nodejs?

I would like to run an executable and its path contains an enviroment variable, for example if I would like to run chrome.exe I would like to write something like this

var spawn = require('child_process').spawn;
spawn('chrome',[], {cwd: '%LOCALAPPDATA%\\Google\\Chrome\\Application', env: process.env})

instead of

var spawn = require('child_process').spawn;
spawn('chrome',[], {cwd: 'C:\\Users\myuser\\AppData\\Local\\Google\\Chrome\\Application', env: process.env}).

Is there a package I can use in order to achieve this?

Upvotes: 17

Views: 7539

Answers (6)

ZYinMD
ZYinMD

Reputation: 5109

These answers are crazy. Just can use path:

const folder = require('path').join(
    process.env.LOCALAPPDATA,
    'Google/Chrome/Application',
);

console.log(folder); // C:\Users\MyName\AppData\Local\Google\Chrome\Application

Upvotes: 1

duckbrain
duckbrain

Reputation: 1279

I realize that the question is asking for Windows environment variables, but I modified @Denys Séguret's answer to handle bash's ${MY_VAR} and $MY_VAR style formats as I thought it might be useful for others who came here.

Note: the two arguments are because there are two groupings based on the variations of the format.

str.replace(/\$([A-Z_]+[A-Z0-9_]*)|\${([A-Z0-9_]*)}/ig, (_, a, b) => process.env[a || b])

Upvotes: 5

Jaredcheeda
Jaredcheeda

Reputation: 2012

Here is a generic helper function for this:

/**
 * Replaces all environment variables with their actual value.
 * Keeps intact non-environment variables using '%'
 * @param  {string} filePath The input file path with percents
 * @return {string}          The resolved file path
 */
function resolveWindowsEnvironmentVariables (filePath) {
  if (!filePath || typeof(filePath) !== 'string') {
    return '';
  }

  /**
   * @param  {string} withPercents    '%USERNAME%'
   * @param  {string} withoutPercents 'USERNAME'
   * @return {string}
   */
  function replaceEnvironmentVariable (withPercents, withoutPercents) {
    let found = process.env[withoutPercents];
    // 'C:\Users\%USERNAME%\Desktop\%asdf%' => 'C:\Users\bob\Desktop\%asdf%'
    return found || withPercents;
  }

  // 'C:\Users\%USERNAME%\Desktop\%PROCESSOR_ARCHITECTURE%' => 'C:\Users\bob\Desktop\AMD64'
  filePath = filePath.replace(/%([^%]+)%/g, replaceEnvironmentVariable);

  return filePath;
}
  • Can be called from anywhere
  • Does basic type checking first, you may want to change what is returned by default in the first if block
  • Functions are named in ways that explain what they do
  • Variables are named in ways that explain what they are
  • Comments added make it clear what outcomes can occur
  • Handles non-environment variables wrapped in percents, since the Windows file system allows for folders to be named %asdf%
  • JSDoc blocks for automated documentation, type checking, and auto-complete in certain editors
  • You may also want to use if (process.platform !== 'win32') {} depending on your need

Upvotes: 3

Paul F. Wood
Paul F. Wood

Reputation: 1775

Adding a TypeScript friendly addition to the excellent answer by Denys Séguret:

let replaced = str.replace(/%([^%]+)%/g, (original, matched) => {
      const r = Process.env[matched]
      return r ? r : ''
})

Upvotes: 0

Denys Séguret
Denys Séguret

Reputation: 382274

You can use a regex to replace your variable with the relevant property of process.env :

let str = '%LOCALAPPDATA%\\Google\\Chrome\\Application'
let replaced = str.replace(/%([^%]+)%/g, (_,n) => process.env[n])

I don't think a package is needed when it's a one-liner.

Upvotes: 21

Alexander Mills
Alexander Mills

Reputation: 100190

On Linux/MacOS, I spawn a process to resolve paths with env variables, is safe - let bash to do the work for you. Obviously less performant, but a lot more robust. Looks like this:

import * as cp from 'child_process';

// mapPaths takes an array of paths/strings with env vars, and expands each one

export const mapPaths = (searchRoots: Array<string>, cb: Function) => {

  const mappedRoots = searchRoots.map(function (v) {
    return `echo "${v}"`;
  });

  const k = cp.spawn('bash');

  k.stdin.end(mappedRoots.join(';'));
  const results: Array<string> = [];
  k.stderr.pipe(process.stderr);

  k.stdout.on('data', (d: string) => {
    results.push(d);
  });

  k.once('error',  (e) => {
    log.error(e.stack || e);
    cb(e);
  });

  k.once('exit', code => {

    const pths = results.map((d) => {
      return String(d || '').trim();
    })
    .filter(Boolean);

    cb(code, pths);

  });
};

Upvotes: -1

Related Questions