Skip to content

Commit 3e4d5ae

Browse files
Add --filter to many commands (#3718)
* Add --filter to pm:list * Add filter-output to more commands * Add filter-output to example command and generated commands. Clean up roll commands a bit (not perfect yet). * Turn off validation for 'filter' in RoleCommands, since it might be a complex expression now. * Don't bother to install all of our dependencies; just install phpcs * Add filter default fields for other filterable commands. These are a best guess; perhaps we should not define a default field unless the use-case for it is clear. * Add a topic for output filters. * Make a combined topic about output formats, fields and filters. * Make @filter-output annotaton implicit if @filter-default-field annotation is present. * Automatically add '@topics docs:output-formats-filters' to any command that defines field labels or filters. * [ci skip] Clarify the filter comparison operators = and *= are case-insensitive. * [ci skip] Add a comparison between filters and grep in the output topic. * Allow the 'site:alias' command to have selectable fields. * Use stable release of output-formatters with unstuctured data support. Fine-tune a couple default fields for the filter option.
1 parent 4a8f293 commit 3e4d5ae

23 files changed

+217
-12
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"composer/semver": "^1.4",
3737
"consolidation/annotated-command": "^2.9.1",
3838
"consolidation/config": "^1.1.0",
39-
"consolidation/output-formatters": "^3.1.12",
39+
"consolidation/filter-via-dot-access-data": "^0.4",
40+
"consolidation/output-formatters": "^3.3.1",
4041
"consolidation/robo": "^1.1.5",
4142
"consolidation/site-alias": "^1.1.5",
4243
"grasmash/yaml-expander": "^1.1.1",

docs/output-formats-filters.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
Output Formats, Fields and Filters
2+
==================================
3+
4+
Drush utilizes a powerful formatting and filtering system that provides the user with a lot of control over how output from various commands is rendered.
5+
6+
* Output formats may be used to select the data type used to print the output. For example, many commands allow the user to select between a human-readable table, or various machine-parsable formats such as yaml and json.
7+
* Output fields may be used to select and order the data columns.
8+
* Output filters may be used to limit which data rows are printed based on logical expressions.
9+
10+
Output Formats
11+
==============
12+
13+
The `--format` option may be used to select the data format used to print the output of a command. Most commands that produce informative output about some object or system can transform their data into different formats. For example, the Drush `version` command may be printed in a human-readable table (the default), or in a json array:
14+
```
15+
$ drush9 version
16+
Drush version : 9.5.0
17+
$ drush9 version --format=json
18+
{
19+
"drush-version": "9.5.0"
20+
}
21+
```
22+
The available output formats are shown in the `help` for each command:
23+
```
24+
$ drush help version
25+
Show drush version.
26+
27+
Options:
28+
--format=<json> Select output format. Available: json, string, var_export, yaml. Default is key-value.
29+
```
30+
31+
Output Fields
32+
=============
33+
34+
If you wish to limit the number of columns produced by a command, use the `--fields` option. List the field names in the order they should be displayed:
35+
```
36+
$ drush9 views:list --fields=machine-name,status
37+
+-------------------+----------+
38+
| Machine name | Status |
39+
+-------------------+----------+
40+
| block_content | Enabled |
41+
| comment | Enabled |
42+
| comments_recent | Enabled |
43+
| content | Enabled |
44+
| content_recent | Enabled |
45+
| files | Enabled |
46+
| frontpage | Enabled |
47+
| taxonomy_term | Enabled |
48+
| user_admin_people | Enabled |
49+
| watchdog | Enabled |
50+
| who_s_new | Enabled |
51+
| who_s_online | Enabled |
52+
| archive | Disabled |
53+
| glossary | Disabled |
54+
+-------------------+----------+
55+
```
56+
The available field names are shown in the `help` text:
57+
```
58+
$ drush9 help views:list
59+
Get a list of all views in the system.
60+
61+
Options:
62+
--fields=FIELDS Available fields: Machine name (machine-name),
63+
Name (label), Description (description), Status
64+
(status), Tag (tag) [default:
65+
"machine-name,label,description,status"]
66+
```
67+
Fields may be named either using their human-readable name, or via their machine name.
68+
69+
Note also that some commands do not display all of their available data columns by default. To show all available fields, use `--fields=*`
70+
71+
There is also a singluar form `--field` available. If this form is used, it will also force the output format to `string`.
72+
```
73+
$ drush9 views:list --field=machine-name
74+
block_content
75+
comment
76+
comments_recent
77+
content
78+
content_recent
79+
files
80+
frontpage
81+
taxonomy_term
82+
user_admin_people
83+
watchdog
84+
who_s_new
85+
who_s_online
86+
archive
87+
glossary
88+
```
89+
90+
Output Filters
91+
==============
92+
93+
A number of Drush commands that output tabular data support a `--filter` option that allows rows from the output to be selected with simple logic expressions.
94+
95+
In its simplest form, the `--filter` option takes a string that indicates the value to filter by in the command's *default filter field*. For example, the `role:list` command's default filter field is `perms`; the output of the `role:list` command may be limited to only those roles that have a specified permission:
96+
```
97+
$ drush role:list --filter='post comments'
98+
authenticated:
99+
label: 'Authenticated user'
100+
perms:
101+
- 'access comments'
102+
- 'access content'
103+
- 'access shortcuts'
104+
- 'access site-wide contact form'
105+
- 'access user contact forms'
106+
- 'post comments'
107+
- 'search content'
108+
- 'skip comment approval'
109+
- 'use text format basic_html'
110+
```
111+
Note that not all commands have a default filter field.
112+
113+
Other fields in the output may be searched by using a simple expression in the `--filter` term. For example, to list only the enabled extensions with the `pm:list` command, you could run:
114+
```
115+
$ drush pm:list --filter='status=enabled'
116+
```
117+
To search for fields that contain a string using the operator `*=`, or match a regular expression with the `~=` operator. For example, to find all views whose machine name contains the word "content":
118+
```
119+
drush views:list --filter='machine-name*=content'
120+
```
121+
To use a regular expression to find any core requirement notice whose title contains either "php" or "gd"
122+
```
123+
drush core:requirements --filter='title~=#(php|gd)#i'
124+
```
125+
Finally, filter expressions may also use logical-and (`&&`) or logical-or (`||`) operations to separate multiple terms. Parenthesis are not supported. For example, to search both the `title` and `severity` fields in the `core:requirements` command:
126+
```
127+
drush core:requirements --filter='title~=#(php|gd)#i&&severity=warning'
128+
```
129+
130+
The `=` and `*=` operators always use case-insensitive comparisons. The `~=` operator is case-sensitive, unless the `i` [PCRE modifier](http://php.net/manual/en/reference.pcre.pattern.modifiers.php) is used, as shown in the previous example.
131+
132+
Comparison of Filters with Grep
133+
-------------------------------
134+
135+
Using the `--filter` feature is similar to using `grep`. The main difference is that the filter feature does a semantic search, which is to say that it explicitly compares against the data in specific fields. In comparison, the `grep` command does a line-based search.
136+
137+
Show only results where the severity is "warning":
138+
139+
`drush core:requirements --filter='severity=warning'`
140+
141+
Show only lines that contain the string "warning" (either in the severity field, or somewhere else on the line):
142+
143+
`drush core:requirements | grep -i warning`
144+
145+
The table below compares and contrasts the two ways of searching.
146+
147+
| Feature | --filter | grep |
148+
| ----------------------- | ------------------- | -------------------------- |
149+
| Regular expressions | Yes, with `~=` | Yes |
150+
| Word-wrapped field data | Searched correctly | Might cause false negative |
151+
| Search just one field | Yes | Might get false positives |
152+
| Search multiple fields | Yes, with `||`/`&&` | Yes (line-based searching) |
153+
| Searching hides header | No | Yes (unless it matches) |

examples/Commands/ArtCommands.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function art($art = '')
6565
* path: Path
6666
* @default-fields name,description
6767
*
68+
* @filter-default-field name
6869
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
6970
*/
7071
public function listArt($options = ['format' => 'table'])

shippable.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ build:
1818
- rm $HOME/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
1919
# Install / update our tools
2020
- composer self-update
21-
- composer install --no-interaction
21+
- mkdir phpcs
22+
- COMPOSER_BIN_DIR=$(pwd)/vendor/bin composer --working-dir=phpcs require "squizlabs/php_codesniffer:^2.7"
2223
# Run code style and linting tools
2324
- composer cs
2425
- composer lint

src/Application.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ public function configureAndRegisterCommands(InputInterface $input, OutputInterf
316316

317317
$discovery = $this->commandDiscovery();
318318
$commandClasses = $discovery->discover($commandfileSearchpath, '\Drush');
319+
$commandClasses[] = \Consolidation\Filter\Hooks\FilterHooks::class;
319320

320321
$this->loadCommandClasses($commandClasses);
321322

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
namespace Drush\Command;
3+
4+
use Consolidation\AnnotatedCommand\CommandInfoAltererInterface;
5+
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
6+
7+
class DrushCommandInfoAlterer implements CommandInfoAltererInterface
8+
{
9+
public function alterCommandInfo(CommandInfo $commandInfo, $commandFileInstance)
10+
{
11+
// If a command has a @filter-default-field annotation, that
12+
// implies that it also has an implicit @filter-output annotation.
13+
if ($commandInfo->hasAnnotation('filter-default-field') && !$commandInfo->hasAnnotation('filter-output')) {
14+
$commandInfo->addAnnotation('filter-output', true);
15+
}
16+
// Automatically add the help topic for output formatters to
17+
// any command that has any annotations related to output filters
18+
if ($commandInfo->hasAnnotation('filter-output') || $commandInfo->hasAnnotation('field-labels')) {
19+
$commandInfo->addAnnotation('topics', 'docs:output-formats-filters');
20+
}
21+
}
22+
}

src/Commands/core/CoreCommands.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CoreCommands extends DrushCommands implements SiteAliasManagerAwareInterfa
2626
* @default-fields name,description
2727
* @aliases core-global-options
2828
*
29+
* @filter-default-field name
2930
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
3031
*/
3132
public function globalOptions($options = ['format' => 'table'])

src/Commands/core/DocsCommands.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ public function configExport()
7777
self::printFile(DRUSH_BASE_PATH. '/docs/config-exporting.md');
7878
}
7979

80+
/**
81+
* Output formatters and filters: how to control the output produced by Drush commands
82+
*
83+
* @command docs:output-formats-filters
84+
* @aliases docs:output
85+
* @aliases docs-output
86+
* @hidden
87+
* @topic
88+
*/
89+
public function outputFormatsFilters()
90+
{
91+
self::printFile(DRUSH_BASE_PATH. '/docs/output-formats-filters.md');
92+
}
93+
8094
/**
8195
* Creating site aliases for running Drush on remote sites.
8296
*

src/Commands/core/SiteCommands.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Consolidation\SiteAlias\SiteAliasFileDiscovery;
88
use Consolidation\SiteAlias\SiteAliasManagerAwareInterface;
99
use Consolidation\SiteAlias\SiteAliasManagerAwareTrait;
10-
use Consolidation\OutputFormatters\StructuredData\ListDataFromKeys;
10+
use Consolidation\OutputFormatters\StructuredData\UnstructuredListData;
1111
use Drush\Utils\StringUtils;
1212
use Symfony\Component\Console\Input\Input;
1313
use Symfony\Component\Console\Output\Output;
@@ -103,9 +103,10 @@ public function siteSet($site = '@none')
103103
* @param string $site Site alias or site specification.
104104
* @param array $options
105105
*
106-
* @return \Consolidation\OutputFormatters\StructuredData\ListDataFromKeys
106+
* @return \Consolidation\OutputFormatters\StructuredData\UnstructuredListData
107107
* @throws \Exception
108108
* @aliases sa
109+
* @filter-default-field id
109110
* @usage drush site:alias
110111
* List all alias records known to drush.
111112
* @usage drush site:alias @dev
@@ -119,13 +120,13 @@ public function siteAlias($site = null, $options = ['format' => 'yaml'])
119120
// multiple sites.
120121
$aliasList = $this->siteAliasManager()->getMultiple($site);
121122
if (is_array($aliasList) && !empty($aliasList)) {
122-
return new ListDataFromKeys($this->siteAliasExportList($aliasList, $options));
123+
return new UnstructuredListData($this->siteAliasExportList($aliasList, $options));
123124
}
124125

125126
// Next check for a specific alias or a site specification.
126127
$aliasRecord = $this->siteAliasManager()->get($site);
127128
if ($aliasRecord !== false) {
128-
return new ListDataFromKeys([$aliasRecord->name() => $aliasRecord->export()]);
129+
return new UnstructuredListData([$aliasRecord->name() => $aliasRecord->export()]);
129130
}
130131

131132
if ($site) {

src/Commands/core/UpdateDBCommands.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public function entityUpdates($options = ['cache-clear' => true])
116116
* description: Description
117117
* type: Type
118118
* @default-fields module,update_id,type,description
119+
* @filter-default-field type
119120
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
120121
*/
121122
public function updatedbStatus($options = ['format'=> 'table', 'entity-updates' => true, 'post-updates' => true])

0 commit comments

Comments
 (0)