0

I have a PHP file which contains an email address as with $to = "example-1_2@example-1_2.com"; and I want to replace the email address to another directly from the terminal.

The following code based on another similar code by Kuslanada worked:

read new_email_address
sed -i 's/$to = ".*";$/$to = "'"$new_email_address"'";/' FILE

Kuslananda told me in comments (paraphrasing):

  • My expression is the concatenation of a single-quoted string, a double-quoted string (the variable), and then another short single-quoted string

As a non professional sysadmin I might use sed once in two years and I find its syntax somewhat difficult to remember and confusing for someone like me which doesn't work with it on a regular basis (the quoting rules clued by Kuslananda there are a bit confusing for me, let along when entire sed commands are normally single-quoted by themselves).
Perhaps there is a way to make Kusalananda's command more "intuitive" or "accessible" for some others and myself. Maybe some backslashes to break the command to pieces would help or maybe another utility would be better for me to do such text replacement.


How to replace an email in a (PHP) file from the command line without sed?

9
  • Your regular expression makes no sense. You have multiple $ in there, what is it supposed to be doing?
    – terdon
    Mar 13, 2021 at 19:12
  • @terdon I thought that the *$ means anything that ends with [CHARACTERS]. Of course I was wrong. I think I should delete this code; I saw you answered about sed but I need a way to do essentially what Kusalananda did but without sed because the syntax of sed is just to hard to me to remember as someone who doesn't use sed frequently (rather, once in a few years).
    – timesharer
    Mar 13, 2021 at 20:12
  • @terdon The first one (now deleted) does not makes sense (as it tries to match literal ^ and $ in non-obvious places), the second one is mine. $ matches a $ in a BRE, unless it's the last character in an expression or a sub-expression, in which case it matches the end of the line.
    – Kusalananda
    Mar 13, 2021 at 20:12
  • @Kusalananda yeah, but that still doesn't make sense since there are no $ in the line after the first character (note that I was referring to the first version of the post).
    – terdon
    Mar 13, 2021 at 20:15
  • 1
    @timesharer your problem isn't related to sed. You will have the same issue no matter what tool you have if you try to combine a shell variable with the tool's syntax. The only way around it would be to use something that can take a variable as an argument (perl or awk etc) but that would include learning another whole new syntax and logic. The general syntax for sed replacement is very simple: sed 's/old/new/'. It is only complicated because you want to use a shell variable.
    – terdon
    Mar 13, 2021 at 20:27

2 Answers 2

2

Quoted shell variables will only be expanded if their outer level of quoting is a double quote. To illustrate:

$ var=foo
$ echo '"$var"'
"$var"
$ echo "'$var'"
'foo'

So, variables are only expanded if the outer level of quotes around them is double.

The next problem is that your regular expression is wrong. $ in a regular expression means "the end of the line", so you can't have multiple $ in there like that. I don't really understand what you are trying to do with that regular expression, so I will just assume you want to change the email address found after the string $to =. If so, try this:

sed -i "s/\\$to = \"[^\"]*\"/\\$to = $new_email_address/" FILE

I have to escape the " (\") and $ (\$to) to avoid the $to to be read as a variable and so the inner " don't close the opening ". I also have to add another layer of escape (\\$) so that the $ isn't seen by the shell and is instead passed to sed. And yes, I realize this is ridiculously complicated.

Alternatively, you can separate it into multiple single and double quoted expressions as I think you were trying to do:

sed 's/\$to = "[^"]*"/$to = '"$new_email_address"'/' FILE
3
  • 1
    The quoted code from my answer is what they says it is: 's/$to = ".*";$/$to = "', "$new_email_address", and '";/'.
    – Kusalananda
    Mar 13, 2021 at 20:07
  • @Kusalananda you're quite right, my bad. I didn't parse it properly. I always avoid this syntax actually for precisely that reason: I always have trouble parsing it by eye.
    – terdon
    Mar 13, 2021 at 20:26
  • 1
    The thing that makes it slightly harder to parse is that there should be a set of double quotes around the variable's value in the replacement string too, so that you get $to = "whatever"; as the result, not $to = whatever.
    – Kusalananda
    Mar 13, 2021 at 20:33
1

You can use the awk utility which looks at lines in terms of fields separated by delimiters you set on the command line with the -F option.

read new_email_address
awk -F '"' -v new="$new_email_address" '
  $1 == "$to = " {$2 = new}
1' OFS='"' your_phpfile

awk -v q=\" -v new="$new_email_address" '
BEGIN {
  new_esc = new
  gsub(/[&]/, "\\\\&", new_esc)
  repl = q new_esc q";"
}
sub(\
    /\$to = ".*";$/, \
     "$to = " repl   \
)+1' your_phpfile
3
  • I thank you for coming to my aid; this looks to me simpler than another similar answer you wrote in another question of mine (which I also venerate): unix.stackexchange.com/questions/639222/…
    – timesharer
    Mar 15, 2021 at 3:14
  • Is there any chance to further simplify the awk command (especially by mean of nesting) so to make it look even more similar to my nested pseudocode here? Sorry if the question is troublesome, I just never ran any awk command before in my life.
    – timesharer
    Mar 15, 2021 at 3:15
  • @timesharer I recommend that you take the time to go through my other answer unix.stackexchange.com/a/639244 which deals with separating out the quotes used in search n replace operations. This thereby solves your problem of having to deal with weaving in n out of single n double quotes.Let me know if some clarifications are needed.I , in fact, considered those to be the most natural fit for your particular requirement at hand.
    – guest_7
    Mar 15, 2021 at 5:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.