bitstarr
bitstarr

Reputation: 374

npm script is ignoring arguments of grep command

I have handy command for looking for TODO notes in my files. It works fine directly from the command line (ubuntu).

This is the plain CLI command:

grep -lir --color --exclude-dir={node_modules,libs} --exclude=package.json 'todo'

And this its occurrence in package.json

{
    "private": true,
    …
    "scripts": {
        "css:dev": "node tasks/postcss.js --mode development",
        "css:build": "node tasks/postcss.js --mode production",
        "js:dev": "webpack --mode development",
        "js:build": "webpack --mode production",
        "lint:css": "npx stylelint assets/css",
        "lint:js": "npx eslint assets/js",
        "images": "node tasks/images.js",
        "sprite": "node tasks/svgsprite.js",
        "todo": "grep -lir --color --exclude-dir={node_modules,libs} --exclude=package.json 'todo'"
    }
}

When running with npm or yarn it ignores the exlude parameter and runs through the node_modules folder.

I have no idea how to fix it.

Upvotes: 3

Views: 2314

Answers (1)

ottomeister
ottomeister

Reputation: 5808

The original command-line execution works because the bash shell expands the expression --exclude-dir={node_modules,libs} to --exclude-dir=node_modules --exclude-dir=libs before invoking grep. grep is happy to take multiple --exclude-dir arguments and it will exclude directories that match any of the specified names.

The problem with the command in package.json arises because npm does not use bash to run the command. It uses plain old sh to run the command.

On some systems sh is just another name for bash, and on those systems putting the original command in package.json will work. Presumably Ahmed's system is one of those.

However, on other systems (notably Debian and its descendants) sh is not the same as bash. On these systems sh is a much simpler shell, and it does not expand the --exclude-dir={node_modules,libs} expression. So grep sees the unexpanded argument and thinks it's being told to exclude a directory whose name is literally {node_modules,libs}. Obviously that's not going to produce the result you want.

There are two obvious ways to fix this. One is to tell npm to tell sh to launch a bash shell to execute the original command:

  "todo": "bash -c \"grep -lir --color --exclude-dir={node_modules,libs} --exclude=package.json 'todo'\""

The other, which I prefer because it doesn't involve multiple levels of shell or nested/escaped quotes, is to explicitly specify both directory names in the command:

  "todo": "grep -lir --color --exclude-dir=node_modules --exclude-dir=libs --exclude=package.json 'todo'"

Upvotes: 4

Related Questions