支持管道
在类 Unix 操作系统(如 Linux 和 macOS)中,管道是一种用于进程间通信(IPC)的机制,允许一个进程的输出被用作另一个进程的输入。
在 Shell 命令中,管道由 |
符号表示。当你使用类似 command1 | command2
的命令时,意味着 command1
的标准输出被连接到 command2
的标准输入。
例如:
cat file.txt | grep "pattern"
在此命令中,file.txt
的内容被作为输入传递到 grep
命令,该命令搜索指定的模式。
修改 bin/cmd.ts 以使 file
参数变为可选:
// Parse command-line options
const argv = await yargs(process.argv.slice(2))
.locale('en')
- .usage('Usage: $0 [options] <pattern> <file>')
+ .usage('Usage: $0 [options] <pattern> [<file>]')
.option('c', {
alias: 'count',
describe: 'Only a count of selected lines is written to standard output.',
@@ -48,10 +48,10 @@ const argv = await yargs(process.argv.slice(2))
type: 'boolean',
default: false
})
- .demandCommand(2, 'Please provide both pattern and file arguments.').argv
+ .demandCommand(1, 'Please provide pattern to search for.').argv
const pattern = argv._[0] as string
-const filePath = argv._[1] as string
+const filePath = argv._[1] !== undefined ? argv._[1] as string : ''
这使得该参数变得可选,允许命令行为其提供一个值或者不提供。
文件路径为空时无法做递归搜索,故修改 bin/cmd.ts:
-const result = argv.recursive as boolean
+const result = (argv.recursive as boolean && filePath !== '')
? grepRecursive(pattern, filePath, options)
: grep(pattern, filePath, options)
在 lib/grep.ts 中,当 filePath
为空时,从标准输入读取内容:
export async function grep (pattern: string, filePath: string, options: Options): Promise<MatchResult> {
const { ignoreCase, invertMatch } = options
- const lines = await _readFileLines(filePath)
+ const lines = filePath === '' ? await _readStdinLines() : await _readFileLines(filePath)
const regexFlags = ignoreCase ? 'gi' : 'g'
const regex = new RegExp(pattern, regexFlags)
let matchingLines: MatchItem[]
@@ -65,3 +66,22 @@ async function _readFileLines (filePath: string): Promise<string[]> {
}
return []
}
+
+async function _readStdinLines (): Promise<string[]> {
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false
+ })
+
+ return await new Promise((resolve) => {
+ const lines: string[] = []
+ rl.on('line', (line) => {
+ lines.push(line)
+ })
+
+ rl.on('close', () => {
+ resolve(lines)
+ })
+ })
+}
重新编译后,现在可以执行以下操作:
cat lib/grep.ts | node dist/bin/cmd.js -in line
其结果:
3: import * as readline from 'readline'
16: const lines = filePath === '' ? await _readStdinLines() : await _readFileLines(filePath)
19: let matchingLines: MatchItem[]
21: matchingLines = _filterLines(regex, lines, false)
23: matchingLines = _filterLines(regex, lines, true)
25: return { [filePath]: matchingLines }
48: (count, lines) => count + lines.length,
53: function _filterLines (regexPattern: RegExp, lines: string[], flag: boolean): MatchItem[] {
54: const candidates: MatchItem[] = lines.map((line, index) => [index + 1, line.trim()])
56: .filter(([_, line]) => regexPattern.test(line) === flag)
59: async function _readFileLines (filePath: string): Promise<string[]> {
70: async function _readStdinLines (): Promise<string[]> {
78: const lines: string[] = []
79: rl.on('line', (line) => {
84: resolve(lines)