why is my perl command line replace not working? - perl

I'm running this but it doesn't seem to replace anything:
perl -p -i -e 's/page?user_id=/page?uid=/g' *
What am I doing wrong here?
I want to replace page?user_id= with page?uid=

The '?' is a special character, indicating that the e needs to be matched 0 or once, so it needs to be escaped if you want to search for a '?' instead of an optional 'e'. Escaping with '\':
try
s/page\?user_id=/page?uid=/g

You can also use Quotemeta:
perl -pi -e 's/\Qpage?user_id=\E/page?uid=/g' file
As a side note I thought why don't you change only user_id to uid.

Related

sed command not working properly on ubuntu

I have one file named `config_3_setConfigPW.ldif? containing the following line:
{pass}
on terminal, I used following commands
SLAPPASSWD=Pwd&0011
sed -i "s#{pass}#$SLAPPASSWD#" config_3_setConfigPW.ldif
It should replace {pass} to Pwd&0011 but it generates Pwd{pass}0011.
The reason is that the SLAPPASSWD shell variable is expanded before sed sees it. So sed sees:
sed -i "s#{pass}#Pwd&0011#" config_3_setConfigPW.ldif
When an "&" is on the right hand side of a pattern it means "copy the matched input", and in your case the matched input is "{pass}".
The real problem is that you would have to escape all the special characters that might arise in SLAPPASSWD, to prevent sed doing this. For example, if you had character "#" in the password, sed would think it was the end of the substitute command, and give a syntax error.
Because of this, I wouldn't use sed for this. You could try gawk or perl?
eg, this will print out the modified file in awk (though it still assumes that SLAPPASSWD contains no " character
awk -F \{pass\} ' { print $1"'${SLAPPASSWD}'"$2 } ' config_3_setConfigPW.ldif
That's because$SLAPPASSWD contains the character sequences & which is a metacharacter used by sed and evaluates to the matched text in the s command. Meaning:
sed 's/{pass}/match: &/' <<< '{pass}'
would give you:
match: {pass}
A time ago I've asked this question: "Is it possible to escape regex metacharacters reliably with sed". Answers there show how to reliably escape the password before using it as the replacement part:
pwd="Pwd&0011"
pwdEscaped="$(sed 's/[&/\]/\\&/g' <<< "$pwd")"
# Now you can safely pass $pwd to sed
sed -i "s/{pass}/$pwdEscaped/" config_3_setConfigPW.ldif
Bear in mind that sed NEVER operates on strings. The thing sed searches for is a regexp and the thing it replaces it with is string-like but has some metacharacters you need to be aware of, e.g. & or \<number>, and all of it needs to avoid using the sed delimiters, / typically.
If you want to operate on strings you need to use awk:
awk -v old="{pass}" -v new="$SLAPPASSWD" 's=index($0,old){ $0 = substr($0,1,s-1) new substr($0,s+length(old))} 1' file
Even the above would need tweaked if old or new contained escape characters.

rename a string in a perl script eg name("this stays the same")

perl -pi-back -e 's/ACTUAL_WORD\(`SOMETHING`\)/EXPECTED_WORD\(`SOMETHING`\)/g;' \
inputfile.txt
I need the "something" to stay the same. something is like a variable that changes.
I think something like this should do it?
perl -pi-back -e 's/ACTUAL_WORD(.*)/EXPECTED_WORD($1)/g;' inputfile.txt
You capture the word in brackets and reuse it via $1 in the replacement. (You may need more brackets - it's unclear if additional are required based on your input).

sed command over multiple lines not working

I am using sed to replace 14 different abbreviations like CA_23456, CB_scaffold34532,... with 'proper' names in a file and it works putting it all on one line.
acc=$1
sed -e 's/CA_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_arizonica/;s/CB_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_bakeri/;s/CM_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_macrocarpa/;s/CS_[A-Z]*[a-z]*[0-9]*/Cupressus_sempervirens/;s/CT_[A-Z]*[a-z]*[0-9]*/Cupressus_torulosa/;s/JD_[A-Z]*[a-z]*[0-9]*/Juniperus_drupacea/;s/JF_[A-Z]*[a-z]*[0-9]*/Juniperus_flaccida/;s/JI_[A-Z]*[a-z]*[0-9]*/Juniperus_indica/;s/JP_[A-Z]*[a-z]*[0-9]*/Juniperus_phoenicea/;s/JX_[A-Z]*[a-z]*[0-9]*/Juniperus_procera/;s/JS_[A-Z]*[a-z]*[0-9]*/Juniperus_scopulorum/;s/MD_[A-Z]*[a-z]*[0-9]*/Microbiota_decussata/;s/XN_[A-Z]*[a-z]*[0-9]*/Xanthocyparis_nootkatensis/;s/XV_[A-Z]*[a-z]*[0-9]*/Xanthocyparis_vietnamensis/' ${acc}.nex > ${acc}_replaced.nex
To make it more readable I'd like to have the command split over multiple lines using '\' (not all the replacements are shown for brevity)
acc=$1
sed -e 's/CA_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_arizonica/;\
s/CB_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_bakeri/;\
s/CM_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_macrocarpa/'\
${acc}.nex > ${acc}_replaced.nex
However, I get an error message: sed: -e expression #1, char 168: unterminated address regex. I have looked at the answers to similar problems on various webforums and tried various things (using 's/.../.../' on every line, leaving ';' out,....) but I can't get it to work. What am I doing wrong?
Drop the \ that escapes the newlines. (They are not actually doing it!, they are interpreted as wrong syntax by sed). However I would suggest to put it into a file and run it like this:
sed -f script.sed input
where script.sed looks like this:
s/CA_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_arizonica/
s/CB_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_bakeri/
s/CM_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_macrocarpa/
Remove the backslashes from the sed code.
Inside singly-quoted shell strings, backslashes are not needed to escape newlines and are not removed because they are not parsed as escape characters. This has the effect that sed sees them as part of its code, and it then expects to find an address regex with a different delimiter than / before the command ends at the next newline (similar to \,/home/, !d). This address regex does not appear (nor an associated command), and so sed complains about invalid code.
Apart from that: The semicolons in the sed code are no longer necessary when you terminate commands with newlines, and anything involving shell variables should be quoted to avoid splitting in case of whitespace.
In sum:
sed -e 's/CA_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_arizonica/
s/CB_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_bakeri/
s/CM_[A-Z]*[a-z]*[0-9]*/Hesperocyparis_macrocarpa/' \
"${acc}.nex" > "${acc}_replaced.nex"

Sed delimiter options

In my code, I need to replace variable assignments with addresses:
sed -i "s/^variable = .*$/variable = http://myaddress/"
Obviously, this does not work because the forward slashes in the address are recognized in the sed command.
I want to keep the $ at the end of the first expression for replacing anything to the end of the line. I also do not want to escape the dollar sign as such, \$ because it will search for a dollar sign.
Also, I don't want to just escape the forward slashes in the address as there are also variables in some places for the addresses.
I've tried using # instead of / but have to include what I don't want to - the \$.
Are there any alternate delimiters I can use that fit my situation?
The $ is interpreted by your shell. Wrap the whole argument to sed with ' to prevent this.
sed -i 's#^variable = .*$#variable = http://myaddress#'
sed -i "s#^variable = .*$#variable = http://myaddress#" file
should work for you.
Note that the $ in the first expression is not literature "dollar", but a regex expression, means, the end of the line.
sed -i 's#^variable = .*#variable = http://myaddress#'

Recursive Search and replace with / and '

I have searched and not found a solution so sorry if this has been answered before, I'm not great at shell.
I'm trying to do a recursive search and replace in all files via SSH.
So far I've got this:
find . -type f | xargs -d "\n" perl -pi -e 's/$this->helper('catalog/product')->getPriceHtml/$this->getPriceHtml/g'
I'm trying to replace this:
$this->helper('catalog/product')->getPriceHtml
with this:
$this->getPriceHtml
But I think its not working because of the slashes and single quotes. I have tried escaping these with \ but to no avail, any ideas?
An alternate delimiter for the s operator could be used to avoid picket fences. $this will be considered to be a variable unless the $ is escaped. The parentheses have to be escaped as well. Else they form a capture group. Since it is a one-liner and quotes have been exhausted, single-quotes have been encoded using hexadecimal escapes. The following should work:
s{\$this->helper\(\x{27}catalog/product\x{27}\)->getPriceHtml}{\$this->getPriceHtml}g;
Or:
s{(?<=\$this)->helper\(\x{27}catalog/product\x{27}\)(?=->getPriceHtml)}{}g;