0

I'd like to replace the database server of a horde config file from "localhost" to a remote server (I use "database.contoso.com" as a placeholder). The file in question is /var/www/horde/config/conf.php. The line in the file looks like this:

$conf['sql']['hostspec'] = 'localhost';

Now I have created a sed line like so:

sed s/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'localhost\'\;/\$conf\[\'sql\'\]\[\
'hostspec\'\]\ \=\ \'database\.contoso\.com\'\;/ /var/www/horde/config/conf.php

But for whatever reason, it does not work -I spare out the -i option for later. While trying to figure out, why it does not work, I did this:

echo "\$conf['sql']['hostspec'] = 'localhost';"|sed s/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'localhost\'\;/\$conf\[\'sql\'\]\[\'hostspec\'\]\ \=\ \'database\.contoso\.com\'\;/

which returns this:

$conf['sql']['hostspec'] = 'localhost';

but it should return:

$conf['sql']['hostspec'] = 'database.contoso.com';

What am I missing?

tripleee
  • 175,061
  • 34
  • 275
  • 318
Inoculator
  • 13
  • 2
  • 1
    Do you _have to_ use sed? `\[` is a normal `[` which has special meaning in regex. You woul dhave to `\\\[` or `'\['` – KamilCuk Oct 09 '22 at 14:35

3 Answers3

1

From Escape a string for a sed replace pattern in this case it would work:

KEYWORD="\$conf['sql']['hostspec'] = 'localhost';"
REPLACE="\$conf['sql']['hostspec'] = 'database.contoso.com';"
ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/"
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
1

The immediate problem is that you are not quoting enough. To match a regex metacharacter literally, you need to pass in a literal backslash \\ followed by a literal, like for example \[. But the simplest solution by far is to use single quotes around your expression, and then only backslash the characters which are regex metacharacters.

Literal single quotes inside single quotes are still challenging. Here, I have chosen to end the single-quoted string, insert a backslash-escaped but otherwise unquoted single quote, and add an opening single quote to continue with another single-quoted string. The shell glues these together into a single string.

echo "\$conf['sql']['hostspec'] = 'localhost';" |
sed 's/\$conf\['\''sql'\''\]\['\''hostspec'\''\] = '\''localhost'\'';/$conf['\''sql'\'']['\''hostspec'\''] = '\''database.contoso.com'\'';/'

A better solution generally is to use backreferences to quote back part of the matched string so you don't have to repeat it.

echo "\$conf['sql']['hostspec'] = 'localhost';" |

sed 's/\(\$conf\['\''sql'\''\]\['\''hostspec'\''\] = '\''\)[^'\'']*'\'';/\1database.contoso.com'\'';/'

Demo: https://ideone.com/RA0MSi

A much much much better solution is to change your PHP script so that this setting can be overridden with an option, an environment variable, and/or a configuration file.

tripleee
  • 175,061
  • 34
  • 275
  • 318
1

This might work for you (GNU sed & shell):

sed -E 's/(\$conf\[('\'')sql\2]\[\2hostspec\2\] = )\2localhost\2;/\1\2database.contoso.com\2;/' file

Use pattern matching to match and replace.

N.B. Certain metacharacters must be escaped/quoted i.e. $,[,] and then because the sed commands are surrounded by single quotes, each single quote (within the substitution command) must be replaced by '\'' (see here for reasoning). Also, back references can be used both in the RHS and the LHS of the substitution command. The back references in the LHS especially allow for the shortening of the overall command and perhaps make the regexp more readable.

potong
  • 55,640
  • 6
  • 51
  • 83