php - 为什么将 docker-compose exec 的输出管道输出到 grep,会破坏它?

标签 php docker grep docker-compose drush

我正在运行此命令以在正在运行的容器中运行 Drush,它基本上是用于 Drupal 的 PHP CLI:

docker-compose -f ../docker-compose.test.yml exec php scripts/bin/vendor/drush.phar -r public_html status-report

如果此命令正常,则输出为有关容器中特定 Drupal 实例的状态信息列表。我不会将它粘贴到这里,因为它很长而且无关紧要。

现在让我们将这些信息通过管道输送到 grep 中进行过滤:

docker-compose -f ../docker-compose.test.yml exec php scripts/bin/vendor/drush.phar -r public_html status-report | grep -e Warning -e Error

结果是:

Cro  Error     L 
Gra  Warning   P 
HTT  Error     F 
HTT  Warning   T 
Dru  Warning   N 
XML  Error     L

哪个不对,好像被剪成碎片了,大部分都不见了。

现在,如果我们要通过添加 -T 标志来禁用伪 tty 的分配:

docker-compose -f ../docker-compose.test.yml exec -T php scripts/bin/vendor/drush.phar -r public_html status-report | grep -e Warning -e Error

输出是正确的:

Cron maintenance      Error     Last run 3 weeks 1 day ago                     
Gravatar              Warning   Potential issues                               
HTTP request status   Error     Fails                                          
HTTPRL - Non          Warning   This server does not handle hanging            
Drupal core update    Warning   No update data available                       
XML sitemap           Error     Last attempted generation on Tue, 04/18/2017 

这是为什么?

奖金问题,可能会由上一个问题的答案回答:使用 -T 是否有任何重要的副作用?

Docker version 18.06.1-ce, build e68fc7a215
docker-compose version 1.22.0

更新 #1:

为了简化操作,我将整个 scripts/bin/vendor/drush.phar -r public_html status-report 的正确输出保存到文件 test.txt 中,并且尝试过:

 docker-compose -f ../docker-compose.test.yml exec php cat test.txt | grep -e Warning -e Error

有趣的是,无论是否使用 -T,输出都是正确的,所以它必须与 Drush/php 有关,尽管我仍然对这可能是什么原因感兴趣。

PHP 7.1.12 (cli) (built: Dec  1 2017 04:07:00) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.1.12, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans

Drush 8.1.17

更新 #2:

为了进一步隔离问题,我将所有内容放在一个 PHP 文件中,简单地打印它,然后:

docker-compose -f ../docker-compose.test.yml exec php php php.php | grep -e Warning -e Error

我得到了正确的输出!

所以它必须与 Drush 打印消息的方式有关,但我看不出它可能是什么。如果我们能解决这个问题,那将非常有趣。

更新 #3:

好的,伙计们,这真的很神奇。在没有任何命令的情况下运行 drush 也会出现此问题,以列出所有可用命令。命令列表在管道输出时被破坏,因此可以在没有实际 Drupal 实例的情况下进行测试。

现在我要向你展示魔法。

drush 中,在 commands/core/help.drush.php 函数 drush_core_help() 中生成可用命令列表的输出>。有这个电话:drush_help_listing_print($command_categories); 我调查了一下。里面有一个调用 drush_print_table($rows, FALSE, array('name' => 20)); 负责生成被破坏的部分输出。

因此在其中,我决定在最后一次调用 drush_print() 之前拦截输出,方法是添加简单的 file_put_contents('/var/www/html/data. txt', $output);

现在轮到对我来说绝对神奇的部分了。

当我执行时:

docker-compose -f ../docker-compose.test.yml exec php scripts/bin/vendor/drush/drush -r public_html

可以在这个文件中检查最后一组命令,在我的例子中是:

 adminrole-update      Update the administrator role permissions.                                                                                                  
 elysia-cron           Run all cron tasks in all active modules for specified site using elysia cron system. This replaces the standard "core-cron" drush handler. 
 generate-redirects    Create redirects.                                                                                                                           
 libraries-download    Download library files of registered libraries.                                                                                             
 (ldl, lib-download)                                                                                                                                               
 libraries-list (lls,  Show a list of registered libraries.                                                                                                        
 lib-list)   

但是,如果我执行相同的命令,但输出将通过管道或重定向,例如:

docker-compose -f ../docker-compose.test.yml exec php scripts/bin/vendor/drush/drush -r public_html | cat

一些不同的东西将被保存到一个文件中:

 adminrole-update      U 
                       p 
                       d 
                       a 
                       t 
                       e 
                       t 
                       h 
                       e 
                       a 
                       d 
                       m 
                       i 
                       n 
                       i 
                       s 
                       t 
                       r 
                       a 
                       t 
                       o 
                       r 
                       r 
(and the rest of the broken output)

因此,在管道/重定向实际发生之前,管道/重定向输出的事实会影响命令的执行。

这怎么可能? O_o

最佳答案

命令行程序根据其输出是否为终端来更改其输出表示的情况并不少见。例如,ls 本身没有任何选项,以柱状格式显示文件。通过管道传输时,输出更改为每行一个文件的列表。你可以在 source code for GNU ls 中看到这个:

case LS_LS:
  /* This is for the 'ls' program.  */
  if (isatty (STDOUT_FILENO))
    {
      format = many_per_line;
      set_quoting_style (NULL, shell_escape_quoting_style);
      /* See description of qmark_funny_chars, above.  */
      qmark_funny_chars = true;
    }
  else
    {
      format = one_per_line;
      qmark_funny_chars = false;
    }
  break;

您可以模拟 ls | 的行为... 带有显式参数 ls -1,这也很常见:隐式更改其输出表示的程序通常提供一种方法来明确地参与该替代演示。

对此的支持不仅仅是约定:它实际上是 ls in POSIX 的要求:

The default format shall be to list one entry per line to standard output; the exceptions are to terminals or when one of the -C, -m, or -x options is specified. If the output is to a terminal, the format is implementation-defined.

这一切看起来很神奇:ls 如何知道它有一个管道之后,因为它在之前 管道?答案非常简单,真的:shell 解析整个命令行,设置管道,然后派生相应的程序,并将输入/输出适本地连接到管道。


那么,命令的哪一部分正在执行备用演示文稿?我怀疑这是您的 exec 环境与 column width calculation 之间的交互匆忙中。在我的本地环境中,drush help | ... 不会产生任何异常结果。您可以尝试通过管道传输到(或通过)cat -vet 以发现输出中的任何异常字符。


也就是说,具体关于 docker-compose:基于 this thread ,您不是唯一遇到此问题或类似问题的人。我没有拖网 docker 源代码,但是 - 通常 - 不分配伪 tty 会使另一端像非交互式 shell 一样工作,这意味着你的 .bash_profile 不会运行,您将无法在运行命令中读取标准输入。这会使事情看起来不正常。

上面链接的线程提到了这种形式的解决方法:

docker exec -i $(docker-compose ...) < input-file

考虑到 -i 的含义,这似乎是合理的,但对于基本脚本而言,这似乎也相当复杂。

-T 使它为你工作的事实向我表明你的 .bash_profile (或类似的特定于登录 shell 的启动文件)中有一些东西改变某些值(可能是 COLUMNS)或改变值以产生观察到的有害影响。您可以尝试从这些文件中删除所有内容,然后将它们添加回去,看看是否有任何特定文件导致了问题。

关于php - 为什么将 docker-compose exec 的输出管道输出到 grep,会破坏它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53027036/

相关文章:

php - 将 SOMETIMES 空值与 PDO 绑定(bind)

php - json_encode 不能转义单引号?

docker - 尝试与主机共享卷时出现docker错误

oracle - 是否有 Oracle XE 数据库的官方 Docker 镜像?

regex - Grep 正则表达式 : List all lines except

javascript - 单击 HighCharts 时显示数据库中的信息

php - 缩短全名,使姓氏只是第一个字母

docker - 使用 docker 的 Julia 集群

linux - 如何去除文本文件的空白空间?

linux - 使用 grep 检索子字符串