child process execution with nodejs

 

aW1wb3J0IHNoZWxsanMgZnJvbSAic2hlbGxqcyI7CnNoZWxsanMuZXhlYyhgZWNobyAtbiAiQUJDImAsIHsgc2lsZW50OiB0cnVlIH0sIGZ1bmN0aW9uIChjb2RlLCBzdGRvdXQsIGVycikgewogIGNvbnNvbGUubG9nKHN0ZG91dCkKfSk7CgovKgrsi6TtlontlZjrqbQg7Lac66Cl65CgIOuCtOyaqeydgD8KMS4gQUJDCjIuIC1uIEFCQwoK7KCV64u17J2AIDIuCgrthLDrr7jrhJDsnYQg7Je06rOgIGVjaG8gLW4gIkFCQyIg66W8IOyeheugpe2VtOu0kOudvCDslrTrlrvqsowg7Lac66Cl65CY64qU6rCAPwpBQkMg7J286rKD7J2064ukCgpzaGVsbGpzIOuKlCDrgrTrtoDsoIHsnLzroZwgbm9kZTogY2hpbGRfcHJvY2VzcyDrpbwg6riw67CY7Jy866GcIO2VnOuLpOqzoCDtlZzri6QKc2hlbGxqcy5leGVjIOuYkO2VnCBjaGlsZF9wcm9jZXNzLmV4ZWMg66W8IOq4sOuwmOycvOuhnCDtlZjripQg66qo7JaR7J2064ukCuq3uOuemOyEnCBjaGlsZF9wcm9jZXNzLmV4ZWMg66GcIO2VtOuzuCDqsrDqs7zrj4Qg64+Z7J287ZWY6rKMIC1uIEFCQyDqsIAg7Lac66Cl65Cc64ukCgrrgpjripQgQUJDIOqwgCDstpzroKXrkKDqsoPsnLzroZwg7JWM6rOgIOyCrOyaqe2VmOqzoOyeiOuKlOuNsCDsnbTroIfqsowg7JiI7IOB6rO8IOuLpOuluCDsnpHrj5nsnYQg7ZW067KE66as66m0IOydtCDqsr3smrAg66eQ6rOgIOuLpOuluOqyveyasOyXkOuPhCDsnbTrn7Ag7JiI7IOBIOuqu+2VnCDsnpHrj5nsnYQg7JWI7ZWg6rGw652864qUIOuztOyepeydhCDriITqsIDtlbTso7zripTqsIAuLgrsi6DrorDrj4TqsIAg7ZmVIOuWqOyWtOyguCDrtojslYjtlbTsoLjrsoTrpqzripQg7IOB7Zmp7J2064ukLgoK7J207Jyg6rCAIOutlOyngCDslYzslYTrs7TrjZgg7KSRLi4g7JWM7JWE64OI64ukLi4KZWNobyAnZWNobyAtbiAiQUJDIicgfCB6c2gKZWNobyAnZWNobyAtbiAiQUJDIicgfCBiYXNoCmVjaG8gJ2VjaG8gLW4gIkFCQyInIHwgc2gK7IS46rCA7KeA66W8IOqwgeqwgSDthLDrr7jrhJDsl5DshJwg7Iuk7ZaJ7ZW0IOuztOyekApzaCDsnZgg6rK97Jqw7JeQIC1uIEFCQyDroZwg7Lac66Cl65CY64qUIOqyg+ydhCDrs7wg7IiYIOyeiOuLpApjaGlsZF9wcm9jZXNzLmV4ZWMg7J2YIOqyveyasCBzaCDsmYAg6rCZ7J2AIOyekeuPmeydhCDtlZjrjZjqsoPsnbTsmIDri6QuLgrqt7jrn7DrjbAgY2hpbGRfcHJvY2Vzcy5zcGF3biDsnZgg6rK97Jqw64qUIHNwYXduKCdlY2hvJywgWyctbicgJ0FCQyddKSDtlbTshJwg7IKs7Jqp7ZWY66m0IHpzaCwgYmFzaCDsnZgg6rKw6rO87JeQ7IScIOyymOufvCDrgpjsmKTripTqsoPsnbTsmIDri6QuCuq3uOufsOuNsCBzcGF3biDsnZgg66y47KCc64qUIHNwYXduKCdzb3VyY2UnLCBbJ3NjcmlwdC5zaCddKSDsnbTrn7Dqsowg7ZW067O46rKw6rO8IOyViOuQkOuLpC4uIOq3uOumrOqzoCDrs7XsnqHtlZwg7ZiV7YOc7J2YIOyJmOyKpO2BrOumve2KuCDsgqzsmqnsnYQg7ZWY6riw7JeQIOyigCDsoIHtlantlZjsp4Ag66q77ZWcIOyduO2EsO2OmOydtOyKpOuLpC4K6re4656Y7IScIOyWtOyoi+uToCDsm5Dsnbjrj4Qg7JWM7JWY7Jy864uIIOydtCDrrLjsoJzrpbwg66qo65GQIO2VtOqysO2VnCDsvZTrk5zrpbwg66eM65OkIOyImCDrsJbsl5Ag7JeG7JeI64ukLgoqLwoKaW1wb3J0IGZzIGZyb20gImZzIjsKaW1wb3J0IHsgcHJvbWlzaWZ5IH0gZnJvbSAidXRpbCI7CmltcG9ydCB7IHNwYXduIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJwpsZXQgcnVubmVyUGF0aDsKbGV0IGV4ZWN1dGVyID0gWydiYXNoJywgJ3pzaCddOwpjb25zdCBhZHZFeGVjID0gYXN5bmMgKC4uLmFyZ3YpID0+IHsKICBpZiAoYXJndi5sZW5ndGggPT09IDApIHJldHVybiB7fTsKICBsZXQgY3dkLCBzdGRpbjsKICBpZiAoYXJndlthcmd2Lmxlbmd0aCAtIDFdLmNvbnN0cnVjdG9yID09PSBPYmplY3QpIHsKICAgIGxldCBvcHQgPSBhcmd2LnNwbGljZShhcmd2Lmxlbmd0aCAtIDEsIDEpWzBdOwogICAgaWYgKG9wdD8uY3dkPy5jb25zdHJ1Y3RvciA9PT0gU3RyaW5nKSBjd2QgPSBvcHQuY3dkOwogICAgaWYgKG9wdD8uc3RkaW4/LmNvbnN0cnVjdG9yID09PSBTdHJpbmcpIHN0ZGluID0gb3B0LnN0ZGluOwogIH0KICBsZXQgY29tbWFuZCA9IGFyZ3Yuc3BsaWNlKDAsIDEpWzBdOwogIGNvbnN0IGxzID0gc3Bhd24oY29tbWFuZCwgYXJndiwgeyBjd2QgfSk7CiAgaWYgKHN0ZGluPy5jb25zdHJ1Y3RvciA9PT0gU3RyaW5nKSB7CiAgICBscy5zdGRpbi53cml0ZShzdGRpbikKICAgIGxzLnN0ZGluLmVuZCgpCiAgfQogIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7CiAgICBsZXQgc3Rkb3V0ID0gW107CiAgICBsZXQgc3RkZXJyID0gW107CiAgICBscy5zdGRvdXQub24oJ2RhdGEnLCAoZGF0YSkgPT4gc3Rkb3V0LnB1c2goZGF0YS50b1N0cmluZygpKSkKICAgIGxzLnN0ZGVyci5vbignZGF0YScsIChkYXRhKSA9PiBzdGRlcnIucHVzaChkYXRhLnRvU3RyaW5nKCkpKQogICAgbHMub24oJ2Nsb3NlJywgKGNvZGUpID0+IHJlc29sdmUoeyBjb2RlLCBzdGRlcnI6IHN0ZGVyci5qb2luKCcnKSwgc3Rkb3V0OiBzdGRvdXQuam9pbignJykgfSkpOwogICAgbHMub24oJ2V4aXQnLCAoY29kZSkgPT4geyB9KTsKICAgIGxzLm9uKCdlcnJvcicsIChjb2RlKSA9PiB7IH0pOwogICAgbHMub24oJ3NwYXduJywgKGNvZGUpID0+IHsgfSk7CiAgICBscy5vbignZGlzY29ubmVjdCcsIChjb2RlKSA9PiB7IH0pOwogIH0pCn07CmNvbnN0IHNoZWxsRXhlYyA9IGFzeW5jIChzdGRpbiwgY3dkKSA9PiB7CiAgaWYgKGN3ZCAmJiAhYXdhaXQgcHJvbWlzaWZ5KGZzLmV4aXN0cykoY3dkKSkgcmV0dXJuIHt9OwogIGlmICghcnVubmVyUGF0aCkgewogICAgZm9yIChsZXQgbmFtZSBvZiBleGVjdXRlcikgewogICAgICBsZXQgcmVzdWx0ID0gYXdhaXQgYWR2RXhlYygnd2hpY2gnLCBuYW1lKTsKICAgICAgaWYgKHJlc3VsdC5jb2RlICE9PSAwKSBjb250aW51ZTsKICAgICAgcnVubmVyUGF0aCA9IHJlc3VsdC5zdGRvdXQudHJpbSgpOwogICAgICBicmVhazsKICAgIH0KICAgIGlmICghcnVubmVyUGF0aCkgcmV0dXJuIHt9OwogIH0KICByZXR1cm4gYXdhaXQgYWR2RXhlYyhydW5uZXJQYXRoLCB7IHN0ZGluLCBjd2QgfSkKfQpleHBvcnQgZGVmYXVsdCBzaGVsbEV4ZWM7CgovKgpb7IKs7Jqp67KVXQogIGNvbnNvbGUubG9nKGF3YWl0IHNoZWxsRXhlYygncHdkJywgJy9Vc2Vycy9rc3QnKSkKICBjb25zb2xlLmxvZyhhd2FpdCBzaGVsbEV4ZWMoJ3B3ZCcpKQogIGNvbnNvbGUubG9nKGF3YWl0IHNoZWxsRXhlYygnbHMgbm90aGluZyAmJiBwd2QnKSkKICBjb25zb2xlLmxvZyhhd2FpdCBzaGVsbEV4ZWMoJ3B3ZFxucHdkJykpCiov
import shelljs from "shelljs"; shelljs.exec(`echo -n "ABC"`, { silent: true }, function (code, stdout, err) { console.log(stdout) }); /* 실행하면 출력될 내용은? 1. ABC 2. -n ABC 정답은 2. 터미널을 열고 echo -n "ABC" 를 입력해봐라 어떻게 출력되는가? ABC 일것이다 shelljs 는 내부적으로 node: child_process 를 기반으로 한다고 한다 shelljs.exec 또한 child_process.exec 를 기반으로 하는 모양이다 그래서 child_process.exec 로 해본 결과도 동일하게 -n ABC 가 출력된다 나는 ABC 가 출력될것으로 알고 사용하고있는데 이렇게 예상과 다른 작동을 해버리면 이 경우 말고 다른경우에도 이런 예상 못한 작동을 안할거라는 보장을 누가해주는가.. 신뢰도가 확 떨어져 불안해져버리는 상황이다. 이유가 뭔지 알아보던 중.. 알아냈다.. echo 'echo -n "ABC"' | zsh echo 'echo -n "ABC"' | bash echo 'echo -n "ABC"' | sh 세가지를 각각 터미널에서 실행해 보자 sh 의 경우에 -n ABC 로 출력되는 것을 볼 수 있다 child_process.exec 의 경우 sh 와 같은 작동을 하던것이였다.. 그런데 child_process.spawn 의 경우는 spawn('echo', ['-n' 'ABC']) 해서 사용하면 zsh, bash 의 결과에서 처럼 나오는것이였다. 그런데 spawn 의 문제는 spawn('source', ['script.sh']) 이런게 해본결과 안됐다.. 그리고 복잡한 형태의 쉘스크립트 사용을 하기에 좀 적합하지 못한 인터페이스다. 그래서 어쨋든 원인도 알았으니 이 문제를 모두 해결한 코드를 만들 수 밖에 없었다. */ import fs from "fs"; import { promisify } from "util"; import { spawn } from 'node:child_process' let runnerPath; let executer = ['bash', 'zsh']; const advExec = async (...argv) => { if (argv.length === 0) return {}; let cwd, stdin; if (argv[argv.length - 1].constructor === Object) { let opt = argv.splice(argv.length - 1, 1)[0]; if (opt?.cwd?.constructor === String) cwd = opt.cwd; if (opt?.stdin?.constructor === String) stdin = opt.stdin; } let command = argv.splice(0, 1)[0]; const ls = spawn(command, argv, { cwd }); if (stdin?.constructor === String) { ls.stdin.write(stdin) ls.stdin.end() } return await new Promise((resolve, reject) => { let stdout = []; let stderr = []; ls.stdout.on('data', (data) => stdout.push(data.toString())) ls.stderr.on('data', (data) => stderr.push(data.toString())) ls.on('close', (code) => resolve({ code, stderr: stderr.join(''), stdout: stdout.join('') })); ls.on('exit', (code) => { }); ls.on('error', (code) => { }); ls.on('spawn', (code) => { }); ls.on('disconnect', (code) => { }); }) }; const shellExec = async (stdin, cwd) => { if (cwd && !await promisify(fs.exists)(cwd)) return {}; if (!runnerPath) { for (let name of executer) { let result = await advExec('which', name); if (result.code !== 0) continue; runnerPath = result.stdout.trim(); break; } if (!runnerPath) return {}; } return await advExec(runnerPath, { stdin, cwd }) } export default shellExec; /* [사용법] console.log(await shellExec('pwd', '/Users/kst')) console.log(await shellExec('pwd')) console.log(await shellExec('ls nothing && pwd')) console.log(await shellExec('pwd\npwd')) */

 

JCBjYXQgL2V0Yy9zaGVsbHMKIyBMaXN0IG9mIGFjY2VwdGFibGUgc2hlbGxzIGZvciBjaHBhc3MoMSkuCiMgRnRwZCB3aWxsIG5vdCBhbGxvdyB1c2VycyB0byBjb25uZWN0IHdobyBhcmUgbm90IHVzaW5nCiMgb25lIG9mIHRoZXNlIHNoZWxscy4KCi9iaW4vYmFzaAovYmluL2NzaAovYmluL2Rhc2gKL2Jpbi9rc2gKL2Jpbi9zaAovYmluL3Rjc2gKL2Jpbi96c2gKCiQgL2Jpbi9iYXNoIC0tdmVyc2lvbgpHTlUgYmFzaCwgdmVyc2lvbiAzLjIuNTcoMSktcmVsZWFzZSAoYXJtNjQtYXBwbGUtZGFyd2luMjEpCkNvcHlyaWdodCAoQykgMjAwNyBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4KCiQgL2Jpbi9jc2ggLS12ZXJzaW9uCnRjc2ggNi4yMS4wMCAoQXN0cm9uKSAyMDE5LTA1LTA4ICh1bmtub3duLWFwcGxlLWRhcndpbikgb3B0aW9ucyB3aWRlLG5scyxkbCxieWUsYWwsa2FuLHNtLHJoLGNvbG9yLGZpbGVjCgokIC9iaW4vZGFzaCAtLXZlcnNpb24KL2Jpbi9kYXNoOiAwOiBJbGxlZ2FsIG9wdGlvbiAtLQoKJCAvYmluL2tzaCAtLXZlcnNpb24KICB2ZXJzaW9uICAgICAgICAgc2ggKEFUJlQgUmVzZWFyY2gpIDkzdSsgMjAxMi0wOC0wMQoKJCAvYmluL3NoIC0tdmVyc2lvbgpHTlUgYmFzaCwgdmVyc2lvbiAzLjIuNTcoMSktcmVsZWFzZSAoYXJtNjQtYXBwbGUtZGFyd2luMjEpCkNvcHlyaWdodCAoQykgMjAwNyBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4KCiQgL2Jpbi90Y3NoIC0tdmVyc2lvbgp0Y3NoIDYuMjEuMDAgKEFzdHJvbikgMjAxOS0wNS0wOCAodW5rbm93bi1hcHBsZS1kYXJ3aW4pIG9wdGlvbnMgd2lkZSxubHMsZGwsYnllLGFsLGthbixzbSxyaCxjb2xvcixmaWxlYwoKJCAvYmluL3pzaCAtLXZlcnNpb24KenNoIDUuOC4xICh4ODZfNjQtYXBwbGUtZGFyd2luMjEuMCkKCuq3uOuemOyEnCDslYTrnpjrpbwg7YWM7Iqk7Yq47ZW067O4IOqysOqzvCBzaOunjCDqsrDqs7zqsIAg64uk66W06rKMIOuCmOyZlOuLpC4KCiQgZWNobyAiZWNobyAtbiAnQUJDJyIgfCAvYmluL2Jhc2gKQUJDCiQgZWNobyAiZWNobyAtbiAnQUJDJyIgfCAvYmluL2NzaApBQkMKJCBlY2hvICJlY2hvIC1uICdBQkMnIiB8IC9iaW4vZGFzaApBQkMKJCBlY2hvICJlY2hvIC1uICdBQkMnIiB8IC9iaW4va3NoCkFCQwokIGVjaG8gImVjaG8gLW4gJ0FCQyciIHwgL2Jpbi9zaAotbiBBQkMKCiQgZWNobyAiZWNobyAtbiAnQUJDJyIgfCAvYmluL3Rjc2gKQUJDCiQgZWNobyAiZWNobyAtbiAnQUJDJyIgfCAvYmluL3pzaApBQkMKCuq3uOuemOyEnCDsmrDrtoTtiKwg7ZS87JSo66W8IOy8nOyEnCBzaCDrpbwg7YWM7Iqk7Yq47ZW067O46rKw6rO8IOunpeqzvOuKlCDri6zrnpDri6Qu
$ cat /etc/shells # List of acceptable shells for chpass(1). # Ftpd will not allow users to connect who are not using # one of these shells. /bin/bash /bin/csh /bin/dash /bin/ksh /bin/sh /bin/tcsh /bin/zsh $ /bin/bash --version GNU bash, version 3.2.57(1)-release (arm64-apple-darwin21) Copyright (C) 2007 Free Software Foundation, Inc. $ /bin/csh --version tcsh 6.21.00 (Astron) 2019-05-08 (unknown-apple-darwin) options wide,nls,dl,bye,al,kan,sm,rh,color,filec $ /bin/dash --version /bin/dash: 0: Illegal option -- $ /bin/ksh --version version sh (AT&T Research) 93u+ 2012-08-01 $ /bin/sh --version GNU bash, version 3.2.57(1)-release (arm64-apple-darwin21) Copyright (C) 2007 Free Software Foundation, Inc. $ /bin/tcsh --version tcsh 6.21.00 (Astron) 2019-05-08 (unknown-apple-darwin) options wide,nls,dl,bye,al,kan,sm,rh,color,filec $ /bin/zsh --version zsh 5.8.1 (x86_64-apple-darwin21.0) 그래서 아래를 테스트해본 결과 sh만 결과가 다르게 나왔다. $ echo "echo -n 'ABC'" | /bin/bash ABC $ echo "echo -n 'ABC'" | /bin/csh ABC $ echo "echo -n 'ABC'" | /bin/dash ABC $ echo "echo -n 'ABC'" | /bin/ksh ABC $ echo "echo -n 'ABC'" | /bin/sh -n ABC $ echo "echo -n 'ABC'" | /bin/tcsh ABC $ echo "echo -n 'ABC'" | /bin/zsh ABC 그래서 우분투 피씨를 켜서 sh 를 테스트해본결과 맥과는 달랐다.