LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (http://www.linuxquestions.org/questions/linux-software-2/)
-   -   Newb having trouble with simple string substitution with sed (http://www.linuxquestions.org/questions/linux-software-2/newb-having-trouble-with-simple-string-substitution-with-sed-4175446489/)

jddancks 01-20-2013 03:05 PM

Newb having trouble with simple string substitution with sed
 
I've been trying to mess with sed for a simple string replacement issue:
change mysql_connect('localhost','user1','pass1');
to mysql_connect('localhost','user2','pass2');

I was skimming this:

http://www.brunolinux.com/02-The_Ter..._with_Sed.html

I tried:

sed -i 's/user1/user2/g' check.php

I get:

sed: 1: "check.php": command c expects \ followed by text

which was right from the tutorial.

I also tried this script:

Code:

#!/bin/bash
mkdir onestopshop/backup
for f in onestopshop/*
do
        cat "" > tmp.txt
        cat $f | cat > $f.bkup
        while read line
        do
                if [ -f $f ]
                then
                        s=""
                        $s = sed 's/user1/user2/' $line | sed 's/pass1/pass2/'
                        if(("${#s}")>1)
                        then
                                cat $s >> tmp.txt
                        else
                                cat $line >> tmp.txt
                        fi
                fi
        done < $f
        cp tmp.txt $f
done
exit 0


colucix 01-20-2013 03:41 PM

The sed command running on MacOS X is slightly different from the GNU-Linux version. The -i option requires an argument which will be used as suffix for a backup copy of the original file. Therefore the 's/user1/user2/g' part is somewhat considered the argument of option -i and check.php the sed command. To avoid that you can either let sed create a backup copy of the original file OR skip that using an empty string as argument, e.g.
Code:

sed -i '' 's/user1/user2/g' check.php
Hope this helps.

David the H. 01-20-2013 05:47 PM

You might also consider using ed instead. It's a true text editor, and can operate directly on the file.

Code:

printf '%s\n' 'g/localhost/s/user1/pass1/g' g/localhost/s/pass1/pass2/g' 'w' | ed check.php
In both sed and ed it generally helps efficiency and accuracy to filter the commands with address fields too, so that they only apply to the lines you want to change.

How to use ed:
http://wiki.bash-hackers.org/howto/edit-ed
http://snap.nlc.dcccd.edu/learn/nlc/ed.html
(also read the info page)

jschiwal 01-20-2013 06:05 PM

sed '/mysql_connect/s/user1/user2/g;s/pass1/pass2/g' check.php > tmpfile
mv tmpfile check.php

The "/mysql_connect/" part causes sed to only edit file that contain a matching string. This can prevent false positives.
The semicolon seperates two sed commands. Do that instead of piping on sed command to another. You can also use the -e argument for additional commands. Each command is run on a line before the next line is read.

I'm not familiar with bsd sed, so I didn't use the "-i" option.

I would't even dare messing with that script you posted. The "s" variable is unneccessary and looks wrongly used, It seems like it's hodpodged together from other scripts.

jddancks 01-21-2013 03:11 PM

Thanks guys. The mac specific sed command worked like a charm. Now I'm working on the script for my debian server. Here's what I have:

Code:

#!/bin/bash
if [ ! -e backup ]
then
        mkdir backup
fi
for f in *
do
        if [[ $f -ne change && ! -d $f ]]
        then
                cp $f backup/$f
                printf '%s\n' 'g/localhost/s/user1/pass1/g' 'g/localhost/s/pass1/pass2/g' 'w' | ed $f
                printf '%s\n' 'g/localhost/s/user1/pass2/g' 'g/localhost/s/user1/user2/g' 'w' | ed $f
        fi
done
exit 0

Which I'm debugging. Something tells me debian doesn't user bash. But I should be able to change that I think.

EDIT: replaced -ne with != and it got past line 8. Debian doesn't have ed, however.

jddancks 01-21-2013 10:03 PM

I hate to double post but I like things to be separate. Whatever. My final solution, thanks to everyone who posted:
Code:

#!/bin/bash
if [ ! -e backup ]
then
        mkdir backup
fi
for f in *
do
        if [ "$f" != "change" ]
        then
                if [ ! -d $f ]
                then
                        cp $f backup/$f
                        sed -i 's/user1/user2/g' $f
                        sed -i 's/pass1/pass2/g' $f
                fi
        fi
done
exit 0


David the H. 01-22-2013 10:20 AM

Quote:

Originally Posted by jschiwal (Post 4874307)
sed '/mysql_connect/s/user1/user2/g;s/pass1/pass2/g' check.php > tmpfile
mv tmpfile check.php

The "/mysql_connect/" part causes sed to only edit file that contain a matching string. This can prevent false positives.

Small correction. The semicolon creates two independent expressions, so the address only limits the first substitution. The second one is still global.

You need to either duplicate the address in the second expression, or group the commands together with brackets.

Code:

sed '/mysql_connect/ { s/user1/user2/g ; s/pass1/pass2/g }' check.php > tmpfile
Now the address applies to both.


Here are a few useful sed references:
http://www.grymoire.com/Unix/Sed.html
http://sed.sourceforge.net/grabbag/
http://sed.sourceforge.net/sedfaq.html
http://sed.sourceforge.net/sed1line.txt


Edit: Oh, and I just remembered that it's possible to combine them in ed too, if you construct the 'g' (global) command correctly:

Code:

printf '%s\n' 'g/localhost/ s/user1/user2/g\' 's/pass1/pass2/g' 'w' | ed -s "$f"
You can have multiple expressions after the 'g', provided you add a backslash at the end of each one to indicate continuation.


All times are GMT -5. The time now is 03:39 PM.