Skip to content

Fix PowerShell command substitution argument parsing#6227

Open
Copilot wants to merge 8 commits intomainfrom
copilot/fix-wp-cli-subcommands-windows
Open

Fix PowerShell command substitution argument parsing#6227
Copilot wants to merge 8 commits intomainfrom
copilot/fix-wp-cli-subcommands-windows

Conversation

Copy link
Contributor

Copilot AI commented Feb 3, 2026

Fix PowerShell argument splitting for space-separated IDs

This PR addresses an issue where WP-CLI commands on Windows PowerShell incorrectly handle space-separated IDs from command substitution.

Problem

  • On PowerShell, $(wp post list --post_type='page' --format=ids) returns IDs as a single string (e.g., "1164 1186")
  • This causes commands like wp post delete $(...) to only process the first ID when the string is cast to int
  • On Linux/Unix, the IDs are correctly separated into individual arguments

Solution

Added Windows-specific argument splitting logic in Runner::back_compat_conversions() to detect and split space-separated numeric IDs.

Implementation Details

The fix:

  • Only activates on Windows (using Utils\is_windows())
  • Only splits arguments that are entirely space-separated numeric IDs (regex: /^\d+(\s+\d+)+$/)
  • Preserves non-numeric strings, single IDs, and strings with leading/trailing spaces as-is
  • Uses array_push() with splat operator for better performance with large numbers of IDs
  • Maintains backward compatibility with Unix-like behavior
  • Includes comprehensive unit tests and Behat scenarios

Changes

  • Implement Windows-specific argument splitting
  • Add comprehensive unit tests
  • Add Behat integration tests
  • Pass all code quality checks
  • Performance optimization using array_push with splat operator
Original prompt

This section details on the original issue you should resolve

<issue_title>wp-cli subcommands on Windows Powershell</issue_title>
<issue_description>Looking at the official WP documentation here, the wp post delete command should be able to take a subcommand to list all the IDs to delete.

This works well on a Linux like system, but doesn't on Windows Powershell.

I know that it is more a Windows+Powershell issue, but that would be nice if we could find a workaround solution for this.

Expectations

# Delete all pages
$ wp post delete $(wp post list --post_type='page' --format=ids)
Success: Trashed post 1164.
Success: Trashed post 1186.

Reality

# Delete all pages
$ wp post delete $(wp post list --post_type='page' --format=ids)
Success: Trashed post 1164 1186.

In that case, post id 1186 has not been deleted because the subcommand result is interpreted as a single string parameter given to the delete command.
The \WP_CLI\CommandWithDBObject::_delete function that should receive $args as an array of IDs receive in reality one string param with the value 1164 1186 which evaluates as 1164 when casted to integer.

Function parameters type hint
Moreover, the wp_delete_post() function that is called underneath, should receive an integer value as first parameter.

/**
 * @param int  $postid       Optional. Post ID. Default 0.
 * @param bool $force_delete Optional. Whether to bypass Trash and force deletion.
 *                           Default false.
 * @return WP_Post|false|null Post data on success, false or null on failure.
 */
function wp_delete_post( $postid = 0, $force_delete = false ) {}
/**
 * Filters whether a post deletion should take place.
 *
 * @since 4.4.0
 *
 * @param WP_Post|false|null $delete       Whether to go forward with deletion. @TODO description
 * @param WP_Post            $post         Post object.
 * @param bool               $force_delete Whether to bypass the Trash.
 */
$check = apply_filters( 'pre_delete_post', null, $post, $force_delete );

I don't know if parameters hints will be enforced later, but there might be an issue there too as this function and the hooks triggered are all expecting an integer value.</issue_description>

Comments on the Issue (you are @copilot in this section)

@danielbachhuber Thanks for the report, @timotheemoulin !

I moved this issue to wp-cli/wp-cli because I think it's related to how arguments are passed/parsed on Powershell. Here's some past work on PowerShell compatibility, if it's helpful: https://github.com/ericmann/WP-PowerShell

Can you share wp cli info ?

Also, what happens when you run wp post delete 1164 1186? Do you see the same error as wp post delete $(wp post list --post_type='page' --format=ids) ?

As a point of reference, here's where WP-CLI reads $GLOBALS['argv']:

$argv = array_slice( $GLOBALS['argv'], 1 );


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits February 3, 2026 00:42
…umeric IDs

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix wp-cli subcommands issue on Windows Powershell Fix PowerShell command substitution argument parsing Feb 3, 2026
Copilot AI requested a review from swissspidy February 3, 2026 00:47
@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@swissspidy swissspidy requested a review from Copilot February 3, 2026 01:26
@swissspidy

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

This comment was marked as resolved.

// Split on whitespace and add each ID as a separate argument
$ids = preg_split( '/\s+/', $arg, -1, PREG_SPLIT_NO_EMPTY );
if ( false !== $ids ) {
$split_args = array_merge( $split_args, $ids );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
For better performance, especially when dealing with a large number of IDs, consider using array_push() with the splat operator (...) instead of array_merge(). array_merge() creates a new array in each iteration inside this loop, which can be inefficient. array_push() modifies the array in place and is generally more performant for appending elements.

array_push( $split_args, ...$ids );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 740369e. Replaced array_merge() with array_push() using the splat operator for better performance when handling large numbers of IDs.

…ray_merge

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Copilot AI requested a review from swissspidy February 23, 2026 18:40
@swissspidy swissspidy marked this pull request as ready for review February 24, 2026 16:27
@swissspidy swissspidy requested a review from a team as a code owner February 24, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wp-cli subcommands on Windows Powershell

3 participants