If you run this command in a Linux terminal:

mkdir -p ./dist/{articles,scripts,stylesheets}

It'll create the following folder tree (in the current directory):

|- articles
|- scripts
|- stylesheets

The problem occurs when I try to do the same using the shelljs npm package.

For example, calling the following function:

shell.mkdir("-p", "./dist/{articles,scripts,stylesheets}");

Results in the following file tree being created:

|- {articles,scripts,stylesheets}

In other words, it's a folder called dist that contains a subfolder called {articles,scripts,stylesheets}.

I've tried escaping the curly braces, like this:

shell.mkdir("-p", "./dist/\{articles,scripts,stylesheets\}");

It didn't work, so I doubled down and escaped the backslash:

shell.mkdir("-p", "./dist/\\{articles,scripts,stylesheets\\}");

That didn't work either, so I doubled down again and added an escaped backslash before the escaped backslash:

shell.mkdir("-p", "./dist/\\\\{articles,scripts,stylesheets\\\\}");

Which didn't work, but it did create a folder with a different name:


How can I fix this problem?

2 Answers

TGrif On Best Solutions

shelljs mkdir() command takes as parameter a list or an array of directory names. It will not attempt to execute any command or sequence builder utility provided by bash, as we can see in source code. So there is no point to try to escape the braces.

Instead you could send the raw command with exec():

shell.exec("bash -c 'mkdir -p ./dist/{articles,scripts,stylesheets}'")
that other guy On

The correct way is to rewrite brace expansion using loops or similar:

const shell = require('shelljs')
for(var dir of ["articles", "scripts", "stylesheets"]) {
  shelljs.mkdir("-p", "./dist/" + dir)

This is fast, robust and portable.

Equivalently, you can use a third party library that expands them for you:

const shell = require('shelljs')
const braces = require('braces')
shell.mkdir("-p", braces("./dist/{articles,scripts,stylesheets}", {expand: true}))

The literal way is to explicitly invoke Bash, since brace expansion is a bash feature:

shelljs.exec("bash -c 'mkdir -p ./dist/{articles,scripts,stylesheets}'")

This is slow, fragile and non-portable because it requires two invocations of two Unix shells and two corresponding levels of escaping.

The point of shelljs is to replace such code with pure JS implementations so that it requires zero invocations of zero shells, so this entirely defeats the purpose of using it in the first place.