bashsed

Search start of string with or without comments and insert text between single quotes within file


I'm trying to automate a manual installation of phpmyadmin and as part of this I'm trying to update settings in the configuration file.

Essentially I would like to be able to search for a specific string of text within the config.inc.php file whether it has a prefixed comment or not, remove the prefixed comment and insert/update the text between the single quotes.

For example I would like to find the following line

// $cfg['Servers'][$i]['controlhost'] = '';

and replace that line with

$cfg['Servers'][$i]['controlhost'] = 'abc123';

So far I've tried the following command but with no luck

sed 's|^\/\/ \[$cfg\['Servers'\]\[$i\]\['controlhost'\] = .*$|'abc123'|g' /var/www/html/example.com/phpmyadmin/config.inc.php

config.inc.php

/* User used to manipulate with storage */
// $cfg['Servers'][$i]['controlhost'] = '';
// $cfg['Servers'][$i]['controlport'] = '';
// $cfg['Servers'][$i]['controluser'] = 'pma';
// $cfg['Servers'][$i]['controlpass'] = 'pmapass';

/* Storage database and tables */
//$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
//$cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark';
// $cfg['Servers'][$i]['relation'] = 'pma__relation';
// $cfg['Servers'][$i]['table_info'] = 'pma__table_info';
// $cfg['Servers'][$i]['table_coords'] = 'pma__table_coords';
// $cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages';
// $cfg['Servers'][$i]['column_info'] = 'pma__column_info';
// $cfg['Servers'][$i]['history'] = 'pma__history';
// $cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs';
// $cfg['Servers'][$i]['tracking'] = 'pma__tracking';
// $cfg['Servers'][$i]['userconfig'] = 'pma__userconfig';
// $cfg['Servers'][$i]['recent'] = 'pma__recent';
// $cfg['Servers'][$i]['favorite'] = 'pma__favorite';
// $cfg['Servers'][$i]['users'] = 'pma__users';
// $cfg['Servers'][$i]['usergroups'] = 'pma__usergroups';
// $cfg['Servers'][$i]['navigationhiding'] = 'pma__navigationhiding';
// $cfg['Servers'][$i]['savedsearches'] = 'pma__savedsearches';
// $cfg['Servers'][$i]['central_columns'] = 'pma__central_columns';
// $cfg['Servers'][$i]['designer_settings'] = 'pma__designer_settings';
// $cfg['Servers'][$i]['export_templates'] = 'pma__export_templates';

Update 1

While surrounding the sed command with single quotes allows this to work, this doesn't allow variable expansion to work. For example if I declare a variable

phpmyadmin_host="localhost"

and then use this in the sed command surrounded with double quotes (to allow variable expansion) this doesn't work.

sed "s|\(\/\/ \)\($cfg\[\x27Servers\x27]\[$i]\[\x27controlhost\x27] = \).*|\2\x27${phpmyadmin_host}\x27;|" /var/www/html/example.com/phpmyadmin/config.inc.php

Update 2

After swapping all of the dollar signs for their hexadecimal counterpart which is \x24

sed "s|\(\/\/ \)\(\x24cfg\[\x27Servers\x27]\[\x24i]\[\x27controlhost\x27] = \).*|\2\x27${phpmyadmin_host}\x27;|" /var/www/html/example.com/phpmyadmin/config.inc.php

$cfg['Servers'][$i]['controlhost'] = 'localhost';

Update 3

For anyone wondering how to remove the double forward slashes comments no matter how many space characters follow in the first capture group you can use .*

From

sed "s|\(\/\/ \)\(capture_group_2\).*|\2|" /etc/some_file

to

sed "s|\(\/\/.*\)\(capture_group_2\).*|\2|" /etc/some_file

The full command would become

sed "s|\(\/\/.*\)\(\x24cfg\[\x27Servers\x27]\[\x24i]\[\x27controlhost\x27] = \).*|\2\x27${phpmyadmin_host}\x27;|" $phpmyadmin_configuration_file

Solution

  • You need to find a way to get the single quotes in there properly: How to escape single quote in sed? "\x27" is a suitable substitute.

    For example, I copied the first four lines from your example into a file named "config.txt" and used this:

    sed 's|// $cfg\[\x27Servers\x27\]\[$i\]\[\x27controlhost\x27\] = .*|hello|' ./config.txt
    

    to get the output:

    /* User used to manipulate with storage */
    hello
    // $cfg['Servers'][$i]['controlport'] = '';
    // $cfg['Servers'][$i]['controluser'] = 'pma';
    // $cfg['Servers'][$i]['controlpass'] = 'pmapass';
    

    Also, you had a spurious "\[" before "$cfg" and you don't need to escape "]". "/" doesn't need to be escaped because "|" is being used as the separator.

    I suspect that you would rather uncomment it and not type the entirety of the part before the "=" again, so you can use capture groups, like this:

    sed 's|\(// \)\($cfg\[\x27Servers\x27]\[$i]\[\x27controlhost\x27] = \).*|\2\x27hello\x27;|' ./config.txt
    

    to get:

    /* User used to manipulate with storage */
    $cfg['Servers'][$i]['controlhost'] = 'hello';
    // $cfg['Servers'][$i]['controlport'] = '';
    // $cfg['Servers'][$i]['controluser'] = 'pma';
    // $cfg['Servers'][$i]['controlpass'] = 'pmapass';
    

    Note that you need to escape each parenthesis for a capture group (Why do I need to escape regex characters in sed to be interpreted as regex characters?), and the "\2" represents the second capture group.