为什么将docker-compose exec的输出传递到grep会中断它?

时间:2018-10-27 23:04:59

标签: 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

2 个答案:

答案 0 :(得分:1)

命令行程序根据其输出是否为终端来更改其输出表示形式并不少见。例如,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的要求:

  

默认格式为在标准输出中每行列出一个条目;终端或指定了-C,-m或-x选项之一的情况除外。如果输出到终端,则格式是实现定义的。

这一切似乎很神奇:ls怎么知道它有一个管道跟随,因为它在管道之前?答案很简单,真的:shell解析整个命令行,设置管道,然后然后派生各个程序,并将输入/输出适当地连接到管道。


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


也就是说,特别是关于.bash_profile:基于this thread,您不是唯一遇到此问题或类似问题的人。我没有拖过docker源代码,但是-通常-不分配伪tty会使另一端像非交互式shell一样工作,这意味着docker exec -i $(docker-compose ...) < input-file 之类的东西将无法运行,而您会赢在运行命令中无法读取标准输入。这样可以使外观看起来不起作用。

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

-i

考虑到-T的含义,这似乎是合理的,但对于基本脚本来说,它似乎也很复杂。

.bash_profile对您有用的事实向我暗示您COLUMNS(或类似的特定于登录Shell的启动文件)中的某些内容正在更改某些值(可能是{{ 1}})或以观察到的有害影响的方式更改值。您可以尝试从这些文件中删除所有内容,然后再将它们重新添加,以查看是否有任何特定原因引起了该问题。

答案 1 :(得分:0)

我没有读过非常详细的问题,但是从浏览一眼来看,我想说-T子命令的exec选项对于要处理stdout至关重要和stderr在执行docker-compose的环境中。