Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shell & command #154

Open
magicdawn opened this issue Mar 12, 2023 · 3 comments
Open

shell & command #154

magicdawn opened this issue Mar 12, 2023 · 3 comments

Comments

@magicdawn
Copy link
Owner

No description provided.

@magicdawn
Copy link
Owner Author

macOS

  • /etc/shells 所有可用的 shell
  • chsh -s /bin/zsh 切换默认 shell 到 zsh

/bin/sh

是 POSIX-compliant shell interpreter, 是指定 shell 的 wrapper.
指定方式, symlink 到 /private/var/select/sh

@magicdawn
Copy link
Owner Author

magicdawn commented Mar 12, 2023

node.js

child_process.execSync(command)

该方法会使用 shell 去执行提供的命令, 默认 '/bin/sh' on Unix, process.env.ComSpec on Windows.

execa

// execa v5 last cjs version
execa.commandSync(command, { shell: false | true | string })

它的 feature 里有一点就是可以不通过 shell 执行 command, 但是需要 escape space.
即使使用引号 + shell:truesome-cmd '文件名 包含空格.txt' 这样还是不行, 还是需要 escape space.
建议使用 child_process.execSync 简单直观.

不 escape 直接按空格 split

// https://github.com/sindresorhus/execa/blob/v5.1.1/index.js#L233-L236
module.exports.commandSync = (command, options) => {
	const [file, ...args] = parseCommand(command);
	return execa.sync(file, args, options);
};

// https://github.com/sindresorhus/execa/blob/v5.1.1/lib/command.js#L29-L46
const SPACES_REGEXP = / +/g;
// Handle `execa.command()`
const parseCommand = command => {
	const tokens = [];
	for (const token of command.trim().split(SPACES_REGEXP)) {
		// Allow spaces to be escaped by a backslash if not meant as a delimiter
		const previousToken = tokens[tokens.length - 1];
		if (previousToken && previousToken.endsWith('\\')) {
			// Merge previous token with current one
			tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`;
		} else {
			tokens.push(token);
		}
	}

	return tokens;
};

@magicdawn
Copy link
Owner Author

execa interleaved output

实际实现起来很简单

import {PassThrough} from 'stream'
import {exec} from 'child_process'

  const bufferAll = new PassThrough({ encoding: 'utf8' })
  let stdall = ''
  bufferAll.on('data', (chunk) => {
    stdall += chunk
  })

  try {
    await new Promise((resolve, reject) => {
      const child = exec(cmd, { shell: '/bin/zsh' }, (err, stdout, stderr) => {
        if (err) return reject(err)
        process.nextTick(() => resolve(undefined))
      })
      child.stdout.pipe(bufferAll)
      child.stderr.pipe(bufferAll)
    })
  } catch (e) {
    console.error('')
    console.error('Error happens: ')
    console.error('')
    console.error(e.stack || e)
    return
  }

  // 使用 stdall 即是混合了 stdout & stderr 的输出

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant