javascriptnode.js

why spawnSync give different result than the actual command


when we have a file structure like that

/foo
  /bar
    a.json
    /baz
      b.json

and then you run git ls-files foo/bar/**/* it return both a.json and b.json

but if you write a script to do that

import { spawnSync } from 'node:child_process';

const pattern = 'foo/bar/**/*';

const lsFiles = spawnSync(
    `git`,
    ['ls-files', pattern],
    {
        encoding: 'utf8',
    },
).stdout;

console.log(lsFiles);

it only return b.json but not a.json

you can test it here https://github.com/robertIsaac/spawn-reprod


Solution

  • When using spawnSync, any arguments that you pass to the executable won't be expanded by your shell, which means that you're running the equivalent of this command:

    git ls-files 'foo/bar/**/*'
    

    (notice the single quotes around the glob pattern)

    The expansion is then done by git, which follows the rules as documented in the gitignore documentation, and those rules work differently from how a typical shell works.

    In this case, foo/bar/**/* will not match files in foo/bar, only files in subdirectories (or subdirectories of subdirectories) of foo/bar, which results in the response that you see:

    $ git ls-files 'foo/bar/**/*'
    foo/bar/baz/b.json
    

    If you change the pattern to foo/bar/**, it will work similarly to your shell pattern:

    $ git ls-files 'foo/bar/**'
    foo/bar/a.json
    foo/bar/baz/b.json