Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sed on xargs variable isn't working inside shell expansion

Tags:

bash

sed

redis

I am attempting to do a batch key rename in Redis, using the shell to sed a replacement for each key name. (There doesn't seem to be a better way to do this in redis internally).

redis-cli -n 5 KEYS "*::staging::*"| xargs -I {} echo "RENAME {} $(echo {} | sed 's/staging/development/g')" 
# Then pipe this command into redis-cli when working

I am expecting an output like this: "RENAME redis::staging::key redis::development::key"

Instead I just get this: "RENAME redis::staging::key redis::staging::key"

The echo {} $(echo {}| sed 's/old/new/') is what is hanging me up... How does one properly achieve taking an xargs output and echo both the original and an edited version of that? $(echo {} |sed) should work, right? (If so, how can I do it correctly?)

like image 741
Excalibur Avatar asked Mar 20 '23 15:03

Excalibur


1 Answers

Solution with sed alone

sed, itself, is able to produce both the unmodified and the modified line:

$ echo "redis::staging::key" | sed 's/^/RENAME /; p; s/staging/development/g'
RENAME redis::staging::key
RENAME redis::development::key

In the above, sed first adds the RENAME string to the beginning of the line. Then, the p command tells sed to print the line as it stands at that time (with "staging" still in it). The next substitution puts in "development" and then that version is printed also.

Update: Suppose we want the output on one line:

$ echo "redis::staging::key" | sed 's/.*/RENAME & &/; s/staging/development/2'
RENAME redis::staging::key redis::development::key

The first s command above adds RENAME to the beginning and then doubles the line. The second replaces the second occurrence of staging with development.

Why didn't the xargs version do the substitution?

xargs -I {} echo "RENAME {} $(echo {} | sed 's/staging/development/g')"

Before xargs executes, bash processes the strings. In particular, it sees $(echo {} | sed 's/staging/development/g') and it executes it ("command substitution") and gets the result {}. So, when xargs is finally run, it sees the command:

xargs -I {} echo "RENAME {} {}"

Consequently, the s/staging/development/g substitution is never made.

Making xargs and shell work together in proper order

There is a fix for this:

$ echo "redis::staging::key" | xargs -I {} sh -c 'echo RENAME {} $(echo {} | sed 's/staging/development/g')'
RENAME redis::staging::key redis::development::key

The above puts the bash commands within single-quotes and passes them as arguments to sh. This way, the string is not processed by the shell until after xargs has made the substitutions.

like image 85
John1024 Avatar answered Apr 05 '23 23:04

John1024