为什么我不能将npm install安装到sed,同时保留颜色和进度更新?

如果我inputnpm i | sed "s/^/ /" npm i | sed "s/^/ /" ,打印到标准输出时, npm i的输出没有间隔。 例如,我得到以下内容:

 $ npm i | sed "s/^/ /" npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules/chokidar/node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"}) npm WARN [name_removed]@0.0.2 No repository field. npm WARN [name_removed]@0.0.2 No license field. 

代替:

 $ npm i | sed "s/^/ /" npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules/chokidar/node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"}) npm WARN [name_removed]@0.0.2 No repository field. npm WARN [name_removed]@0.0.2 No license field. 

编辑:警告将stderr(杜…),所以我需要使用npm i 2>&1 | sed 's/^/ / npm i 2>&1 | sed 's/^/ / ,但是这从输出中删除了颜色,我没有看到进度条,你可以在下面的gif中看到。

在这里输入图像说明 在这里输入图像说明

Edit2:颜色通过npm i --color=always | sed 's/^/ /固定npm i --color=always | sed 's/^/ / npm i --color=always | sed 's/^/ / ,但我仍然没有看到进度条。 另外,似乎在所有输出之后的行中添加了行…我认为这是由输出的颜色代码引起的? 你可以在下面的gif中看到这个现象:

在这里输入图像说明

当前状态: 在这里输入图像说明

这里遇到了几个问题,其中大部分与npm处理输出的方式有关。

发现警告

首先请注意, npm输出警告并更新stderr 进度 ,而只有最终结果才会转化为stdout 。 所以,为了处理警告,你必须将stderrredirect到stdout ,如下所示:

 npm install 2>&1 | sed 's/^/ /' 

保持颜色

但是现在用stderrpipe道sed过程,你会注意到npm忽略了着色! 然而,这是大多数命令行工具(如lsgrep等)的标准行为。 只有在输出到TTY设备(即用户,而不是文件或pipe道)时,他们才会输出ANSI转义(彩色)序列。 确定文件描述符是否连接到TTY的常用方法是通过isatty(int fd)函数。 事实certificate( 在 一些 挖掘之后 ) npm使用相同的机制。

为了解决着色问题,我们有两个select:

(1)我们可以用--color=always选项强制颜色输出(类似于lsgrep等):

 npm install --color=always 2>&1 | sed 's/^/ /' 

或者, (2)我们可以使用一个名为script的工具来伪造任何程序/脚本运行的TTY输出设备:

 script -feqc 'npm install' /dev/null | sed 's/^/ /' 

注意,我们不必将stderrredirect到stdout了, script为我们做了。 另外, script会将完整的输出保存到我​​们select的文件中(在这种情况下,我们不需要它,所以我们说/dev/null )。 (顺便说一下, -f将在每次写入后刷新输出, -e将确保该命令的退出代码返回到父/ shell, -q强制安静模式,没有信息消息,并使用-c cmd我们提供命令来运行。)

保留进度更新

好吧,现在我们有警告缩进和颜色保存,但我们已经失去了我们的进度(酒吧)更新!

为什么发生这种事? 那么,因为npm在一行中输出完整的进度。 在每次进度更新时,它将移动到字符位置零(同一行!)并打印新的进度。 对于sed完整的进度只是一条线 ,而且由于sed是面向行的,所以在进行任何处理(和输出)之前,它一直等到行结束( \n )。

显然,我们需要降低一个层次 – 逐字处理。 为了达到缩进的效果,我们将用\n<4 spaces>replace\n每个出现位置。

通常,对于字符翻译,我们可以使用tr ,但是在这里我们需要的不仅仅是这些,因为在某些情况下( \n )我们需要将一个字符扩展到几个字符。 一种方法是使用这个简单的bash脚本/函数:

 #!/bin/bash # read each character of stdin, indenting each line interactive_indent() { local space=' ' echo -n "$space" while IFS= read -r -d '' -n1 chr; do [[ $chr == $'\n' ]] && chr="\\n\\r$space" [[ $chr == $'\r' ]] && chr="\\r$space" echo -ne "$chr" done echo -ne '\r' } 

例如:

 $ echo -e 'one\ntwo\rthree' | interactive_indent one three 

最后,我们来看看我们的交互式npm过程的解决scheme

 script -feqc 'npm install' /dev/null | interactive_indent 

这将传递每个字符(显示进度),同时缩进每行(在\n\r )。

请注意,我们的interactive_indent函数比简单的\n to- \n<spaces>replace器复杂一点。 我们还必须处理由npm大量使用的回车符( \r ),用于进度更新和依赖树绘制,并确保每个新行从零开始(因此每个\n旁边都有\n )。