Skip to content

Commit 6b6a2b3

Browse files
authored
Merge pull request #210 from wp-cli/copilot/fix-search-replace-hyphen-issue
Add --old and --new flags to support strings starting with hyphens
2 parents fa1cffc + b99dd78 commit 6b6a2b3

3 files changed

Lines changed: 207 additions & 8 deletions

File tree

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Quick links: [Using](#using) | [Installing](#installing) | [Contributing](#contr
1010
## Using
1111

1212
~~~
13-
wp search-replace <old> <new> [<table>...] [--dry-run] [--network] [--all-tables-with-prefix] [--all-tables] [--export[=<file>]] [--export_insert_size=<rows>] [--skip-tables=<tables>] [--skip-columns=<columns>] [--include-columns=<columns>] [--precise] [--recurse-objects] [--verbose] [--regex] [--regex-flags=<regex-flags>] [--regex-delimiter=<regex-delimiter>] [--regex-limit=<regex-limit>] [--format=<format>] [--report] [--report-changed-only] [--log[=<file>]] [--before_context=<num>] [--after_context=<num>]
13+
wp search-replace [<old>] [<new>] [<table>...] [--old=<value>] [--new=<value>] [--dry-run] [--network] [--all-tables-with-prefix] [--all-tables] [--export[=<file>]] [--export_insert_size=<rows>] [--skip-tables=<tables>] [--skip-columns=<columns>] [--include-columns=<columns>] [--precise] [--recurse-objects] [--verbose] [--regex] [--regex-flags=<regex-flags>] [--regex-delimiter=<regex-delimiter>] [--regex-limit=<regex-limit>] [--format=<format>] [--report] [--report-changed-only] [--log[=<file>]] [--before_context=<num>] [--after_context=<num>]
1414
~~~
1515

1616
Searches through all rows in a selection of tables and replaces
@@ -25,16 +25,24 @@ change primary key values.
2525

2626
**OPTIONS**
2727

28-
<old>
28+
[<old>]
2929
A string to search for within the database.
3030

31-
<new>
31+
[<new>]
3232
Replace instances of the first string with this new string.
3333

3434
[<table>...]
3535
List of database tables to restrict the replacement to. Wildcards are
3636
supported, e.g. `'wp_*options'` or `'wp_post*'`.
3737

38+
[--old=<value>]
39+
An alternative way to specify the search string. Use this when the
40+
search string starts with '--' (e.g., --old='--some-text').
41+
42+
[--new=<value>]
43+
An alternative way to specify the replacement string. Use this when the
44+
replacement string starts with '--' (e.g., --new='--other-text').
45+
3846
[--dry-run]
3947
Run the entire search/replace operation and show report, but don't save
4048
changes to the database.
@@ -141,6 +149,9 @@ change primary key values.
141149
# Search/replace to a SQL file without transforming the database
142150
$ wp search-replace foo bar --export=database.sql
143151

152+
# Search/replace string containing hyphens
153+
$ wp search-replace --old='--old-string' --new='new-string'
154+
144155
# Use precise mode for complex serialized data
145156
$ wp search-replace 'oldurl.com' 'newurl.com' --precise
146157

features/search-replace.feature

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,3 +1392,145 @@ Feature: Do global search/replace
13921392
"""
13931393
Success: Made 0 replacements.
13941394
"""
1395+
1396+
@require-mysql
1397+
Scenario: Search/replace strings starting with hyphens using --old and --new flags
1398+
Given a WP install
1399+
And I run `wp post create --post_title="Test Post" --post_content="This is --old-content and more text" --porcelain`
1400+
Then save STDOUT as {POST_ID}
1401+
1402+
When I run `wp search-replace --old='--old-content' --new='--new-content'`
1403+
Then STDOUT should contain:
1404+
"""
1405+
wp_posts
1406+
"""
1407+
And the return code should be 0
1408+
1409+
When I run `wp post get {POST_ID} --field=post_content`
1410+
Then STDOUT should contain:
1411+
"""
1412+
--new-content
1413+
"""
1414+
And STDOUT should not contain:
1415+
"""
1416+
--old-content
1417+
"""
1418+
1419+
@require-mysql
1420+
Scenario: Error when neither positional args nor flags provided
1421+
Given a WP install
1422+
1423+
When I try `wp search-replace`
1424+
Then STDERR should contain:
1425+
"""
1426+
Please provide both <old> and <new> arguments
1427+
"""
1428+
And STDERR should contain:
1429+
"""
1430+
--old
1431+
"""
1432+
And STDERR should contain:
1433+
"""
1434+
--new
1435+
"""
1436+
And the return code should be 1
1437+
1438+
@require-mysql
1439+
Scenario: Error when only --old flag provided without --new
1440+
Given a WP install
1441+
1442+
When I try `wp search-replace --old='test-value'`
1443+
Then STDERR should contain:
1444+
"""
1445+
Please provide the <new> argument
1446+
"""
1447+
And the return code should be 1
1448+
1449+
@require-mysql
1450+
Scenario: Error when only --new flag provided without --old
1451+
Given a WP install
1452+
1453+
When I try `wp search-replace --new='test-value'`
1454+
Then STDERR should contain:
1455+
"""
1456+
Please provide the <old> argument
1457+
"""
1458+
And the return code should be 1
1459+
1460+
@require-mysql
1461+
Scenario: Error when both flags and positional arguments provided
1462+
Given a WP install
1463+
1464+
When I try `wp search-replace --old='flag-old' --new='flag-new' 'positional-arg'`
1465+
Then STDERR should contain:
1466+
"""
1467+
Cannot use both positional arguments and --old/--new flags
1468+
"""
1469+
And the return code should be 1
1470+
1471+
@require-mysql
1472+
Scenario: Error when empty string provided via --old flag
1473+
Given a WP install
1474+
1475+
When I try `wp search-replace --old='' --new='replacement'`
1476+
Then STDERR should contain:
1477+
"""
1478+
Please provide the <old> argument
1479+
"""
1480+
And the return code should be 1
1481+
1482+
@require-mysql
1483+
Scenario: No error when empty string provided via --new flag
1484+
Given a WP install
1485+
1486+
When I try `wp search-replace --old='search' --new=''`
1487+
Then STDERR should not contain:
1488+
"""
1489+
Please provide the <new> argument
1490+
"""
1491+
1492+
@require-mysql
1493+
Scenario: Search/replace string starting with single hyphen works with positional args
1494+
Given a WP install
1495+
And I run `wp post create --post_title="Test Post" --post_content="This is -single-hyphen content" --porcelain`
1496+
Then save STDOUT as {POST_ID}
1497+
1498+
When I run `wp search-replace '-single-hyphen' '-replaced-hyphen'`
1499+
Then STDOUT should contain:
1500+
"""
1501+
wp_posts
1502+
"""
1503+
And the return code should be 0
1504+
1505+
When I run `wp post get {POST_ID} --field=post_content`
1506+
Then STDOUT should contain:
1507+
"""
1508+
-replaced-hyphen
1509+
"""
1510+
And STDOUT should not contain:
1511+
"""
1512+
-single-hyphen
1513+
"""
1514+
1515+
@require-mysql
1516+
Scenario: Allow mixing one flag with one positional argument
1517+
Given a WP install
1518+
And I run `wp post create --post_title="Test Post" --post_content="This is --old-content text" --porcelain`
1519+
Then save STDOUT as {POST_ID}
1520+
1521+
When I run `wp search-replace --old='--old-content' 'new-content'`
1522+
Then STDOUT should contain:
1523+
"""
1524+
wp_posts
1525+
"""
1526+
And the return code should be 0
1527+
1528+
When I run `wp post get {POST_ID} --field=post_content`
1529+
Then STDOUT should contain:
1530+
"""
1531+
new-content
1532+
"""
1533+
And STDOUT should not contain:
1534+
"""
1535+
--old-content
1536+
"""

src/Search_Replace_Command.php

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,24 @@ class Search_Replace_Command extends WP_CLI_Command {
134134
*
135135
* ## OPTIONS
136136
*
137-
* <old>
137+
* [<old>]
138138
* : A string to search for within the database.
139139
*
140-
* <new>
140+
* [<new>]
141141
* : Replace instances of the first string with this new string.
142142
*
143143
* [<table>...]
144144
* : List of database tables to restrict the replacement to. Wildcards are
145145
* supported, e.g. `'wp_*options'` or `'wp_post*'`.
146146
*
147+
* [--old=<value>]
148+
* : An alternative way to specify the search string. Use this when the
149+
* search string starts with '--' (e.g., --old='--some-text').
150+
*
151+
* [--new=<value>]
152+
* : An alternative way to specify the replacement string. Use this when the
153+
* replacement string starts with '--' (e.g., --new='--other-text').
154+
*
147155
* [--dry-run]
148156
* : Run the entire search/replace operation and show report, but don't save
149157
* changes to the database.
@@ -250,6 +258,9 @@ class Search_Replace_Command extends WP_CLI_Command {
250258
* # Search/replace to a SQL file without transforming the database
251259
* $ wp search-replace foo bar --export=database.sql
252260
*
261+
* # Search/replace string containing hyphens
262+
* $ wp search-replace --old='--old-string' --new='new-string'
263+
*
253264
* # Use precise mode for complex serialized data
254265
* $ wp search-replace 'oldurl.com' 'newurl.com' --precise
255266
*
@@ -262,12 +273,47 @@ class Search_Replace_Command extends WP_CLI_Command {
262273
* fi
263274
*
264275
* @param array<string> $args Positional arguments.
265-
* @param array{'dry-run'?: bool, 'network'?: bool, 'all-tables-with-prefix'?: bool, 'all-tables'?: bool, 'export'?: string, 'export_insert_size'?: string, 'skip-tables'?: string, 'skip-columns'?: string, 'include-columns'?: string, 'precise'?: bool, 'recurse-objects'?: bool, 'verbose'?: bool, 'regex'?: bool, 'regex-flags'?: string, 'regex-delimiter'?: string, 'regex-limit'?: string, 'format': string, 'report'?: bool, 'report-changed-only'?: bool, 'log'?: string, 'before_context'?: string, 'after_context'?: string} $assoc_args Associative arguments.
276+
* @param array{'old'?: string, 'new'?: string, 'dry-run'?: bool, 'network'?: bool, 'all-tables-with-prefix'?: bool, 'all-tables'?: bool, 'export'?: string, 'export_insert_size'?: string, 'skip-tables'?: string, 'skip-columns'?: string, 'include-columns'?: string, 'precise'?: bool, 'recurse-objects'?: bool, 'verbose'?: bool, 'regex'?: bool, 'regex-flags'?: string, 'regex-delimiter'?: string, 'regex-limit'?: string, 'format': string, 'report'?: bool, 'report-changed-only'?: bool, 'log'?: string, 'before_context'?: string, 'after_context'?: string} $assoc_args Associative arguments.
266277
*/
267278
public function __invoke( $args, $assoc_args ) {
268279
global $wpdb;
269-
$old = array_shift( $args );
270-
$new = array_shift( $args );
280+
281+
// Support --old and --new flags as an alternative to positional arguments.
282+
// This allows users to search/replace strings that start with '--'.
283+
$old_flag = Utils\get_flag_value( $assoc_args, 'old' );
284+
$new_flag = Utils\get_flag_value( $assoc_args, 'new' );
285+
286+
// Check if both flags and positional arguments are provided.
287+
$both_flags_provided = null !== $old_flag && null !== $new_flag;
288+
$has_positional_args = ! empty( $args );
289+
if ( $both_flags_provided && $has_positional_args ) {
290+
WP_CLI::error( 'Cannot use both positional arguments and --old/--new flags. Please use one method or the other.' );
291+
}
292+
293+
// Determine old and new values.
294+
$old = null !== $old_flag ? $old_flag : array_shift( $args );
295+
$new = null !== $new_flag ? $new_flag : array_shift( $args );
296+
297+
// Validate that both old and new values are provided and not empty.
298+
if ( null === $old || null === $new || '' === $old ) {
299+
$missing = array();
300+
if ( null === $old || '' === $old ) {
301+
$missing[] = '<old>';
302+
}
303+
// new value is allowed to be empty.
304+
if ( null === $new ) {
305+
$missing[] = '<new>';
306+
}
307+
$error_msg = count( $missing ) === 2
308+
? 'Please provide both <old> and <new> arguments.'
309+
: sprintf( 'Please provide the %s argument.', $missing[0] );
310+
311+
$error_msg .= "\n\nNote: If your search or replacement string starts with '--', use the flag syntax instead:"
312+
. "\n wp search-replace --old='--text' --new='replacement'";
313+
314+
WP_CLI::error( $error_msg );
315+
}
316+
271317
$total = 0;
272318
$report = array();
273319
$this->dry_run = Utils\get_flag_value( $assoc_args, 'dry-run', false );

0 commit comments

Comments
 (0)