简体   繁体   English

我可以将node.js脚本输出管道输入`less`而不输入`| 跑的时候少吗?

[英]Can I pipe my node.js script output in to `less` without typing `| less` when run?

For example, my script will generate a lot of output using process.stdout.write() . 例如,我的脚本将使用process.stdout.write()生成大量输出。 I know I can pipe them into less by running it as node mycode.js | less -N 我知道我可以管他们到less运行它作为node mycode.js | less -N node mycode.js | less -N . node mycode.js | less -N

But is there a way so that I can do the piping inside of my code, so that other people can run my code normally node mycode.js and still get my output piped into less ? 但有没有办法让我可以在我的代码中进行管道,以便其他人可以正常运行我的代码node mycode.js并仍然将我的输出管道输入less

Yes, you can pipe the output of your node program into the input less via the normal child_process core module's API. 是的,您可以通过普通的child_process核心模块的API将节点程序的输出传输到输入中。 However, the issue will be the controlling pty. 然而,问题将是控制pty。 If you are running your node program, it will control the pty and less won't have access to the pty, so interacting with less by typing commands won't work. 如果您正在运行节点程序,它将控制pty而less将无法访问pty,因此通过键入命令与less进行交互将不起作用。 AFAIK (others may very well know better than I) there's no clean way to do this from within your node program, so I would just write a wrapper shell script to do it and call it done. AFAIK(其他人可能比我更清楚)在你的节点程序中没有干净的方法来做这个,所以我只想编写一个包装shell脚本来完成它并调用它。

The closest possibility I found in npm is default-pager but from my quick test harness, it does not seem to work. 我在npm中找到的最接近的可能性是default-pager,但是从我的快速测试工具中,它似乎不起作用。 :-( :-(

This is possible from within Node, if you're willing to use a compiled extension: node-kexec . 如果您愿意使用已编译的扩展名: node-kexec ,则可以从Node中进行此操作

I preform almost precisely the tasks you want to in my project's executable as follows (forgive the CoffeeScript): 在项目的可执行文件中几乎准确地完成了你想要的任务,如下所示(原谅CoffeeScript):

page = (cb)->

   # If we reach this point in the code and $_PAGINATED is already set, then we've
   # successfully paginated the script and should now actually run the code meant
   # to be run *inside* a pager.
   if process.env['_PAGINATED']?
      return cb()

   # I use tricks like this to control the pager itself; they can be super-dirty,
   # though, and mutating shell-command lines without a *lot* of careful
   # invocation logic is generally a bad idea unless you have a good reason:
   pager = process.env.PAGER || 'less --chop-long-lines'
   pager = pager.replace /less(\s|$)/, 'less --RAW-CONTROL-CHARS$1'

   # I use this elsewhere in my code-base to override `term.columns` if it is
   # unset; because the pager often doesn't properly report terminal-width
   process.env['PAGINATED_COLUMNS'] = term.columns
   process.env['_PAGINATED'] = 'yes'

   # This is a horrible hack. Thanks, Stack Overflow.
   #    <https://stackoverflow.com/a/22827128>
   escapeShellArg = (cmd)-> "'" + cmd.replace(/\'/g, "'\\''") + "'"

   # This is how we *re-invoke* precisely the exact instructions that our script /
   # executable was originally given; in addition to ensuring that `process.argv`
   # is the same by doing this, `kexec` will already ensure that our replacement
   # inherits our `process.stdout` and etc.
   #
   # (These arguments are invoked *in a shell*, as in `"sh" "-c" ...`, by
   # `kexec()`!)
   params = process.argv.slice()
   params = params.map (arg)-> escapeShellArg arg
   params.push '|'
   params.push pager

   log.debug "!! Forking and exec'ing to pager: `#{pager}`"
   log.wtf "-- Invocation via `sh -c`:", params.join ' '

   kexec params.join ' '

This is invoked as simply as you'd expect; 这可以像你期望的那样简单地调用; something like page(print_help_text) (which is how I'm using it). 类似于page(print_help_text) (这就是我使用它的方式)。

There's also a couple obvious gotchas: it's not going to magically fork your program where it is invoked , it's going to re-execute the entire program up to the point where it got invoked; 还有一些显而易见的问题:它不会神奇地将你的程序分配到它被调用的位置 ,它会重新执行整个程序直到它被调用的程度; so you'll want to make sure that anything happening before invoking page() is deterministic in nature; 因此,您需要确保调用page() 之前发生的任何事情本质上都是确定性的; ie precisely the same things will occur if the program is re-invoked with the same set of command-line arguments. 即,如果使用同一组命令行参数重新调用程序,则会发生相同的事情。 (It's a convenience, not magic.) You probably also want to make sure the code leading up to page() is idempotent, ie doesn't have any undesired side-effects when run twice. (这是一种方便,而不是魔术。)您可能还希望确保通向page()的代码是幂等的,即在运行两次时没有任何不良的副作用。

(If you want to do this without compiling a native extension, try and get Node core to add an exec function like Ruby's. :P ) (如果你想在不编译原生扩展的情况下这样做,请尝试让Node核心添加像Ruby的exec函数。 :P


Nota bene: If you do decide to do this, please make it configurable with the standard --[no-]pager flag. 诺塔好处:如果决定这样做,请使其配置与标准--[no-]pager标志。 Pagers can be a nice convenience, but not everybody wants to use one. 寻呼机可以很方便,但不是每个人都想使用它。

On the same note, please realize that compiled dependencies can cause a lot of people trouble; 同样,请注意编译后的依赖会导致很多人麻烦; personally, I keep kexec in my package.json 's optionalDependencies , and then use a try/catch (or a convenience like optional ) to source it. 个人而言,我将kexec保存在我的package.jsonoptionalDependencies ,然后使用try/catch (或类似的optional方便)来获取它。 That way, if it fails to install on the user's system, your code still runs as expected, just without the nicety of the pager. 这样,如果它无法安装在用户的系统上,您的代码仍然按预期运行,只是没有寻呼机的精确性。

Among the four APIs from node's built-in child_process , you would want to use spawn that gives you the choice to run less in the current shell which invokes the script. 在node的内置child_process的四个API中,你可能想要使用spawn ,它可以让你选择在调用脚本的当前shell中运行less的。 All other choices like exec , fork will simply start a new invisible shell. 所有其他选择,如execfork只会启动一个新的隐形shell。

const spawn = require("child_process").spawn;

spawn(`cat <<< "${yourOutputString}" | less -r`, {   // <<< read the *HERE* string to cat
                                                     // less -r specifies colored output
            stdio: 'inherit', // use the current shell for stdio
            shell: true
    });

Now you can invoke your script simply with node script.js . 现在,您只需使用node script.js调用脚本即可。

This lengthy tutorial gives you a thorough view of the child_process API. 这个冗长的教程为您提供了child_process API的全面视图。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM