我正在运行此命令以在正在运行的容器中运行 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/