sed replace single/double quoted text?

后端 未结 4 1257
南旧
南旧 2021-02-04 06:11

Having some difficulty in replacing some single/double quoted text with sed and was wondering what\'s the correct method for these 2 examples

to change

相关标签:
4条回答
  • 2021-02-04 06:31

    You either need to quote the quotes or escape the quotes. Did you try checking what sed sees when you give it that last command?

    sed -i 's/'ADMIN_USERNAME','memcache'/'ADMIN_USERNAME','u'/g' /var/www/html/memcache.php

    Try this:

    echo sed -i 's/'ADMIN_USERNAME','memcache'/'ADMIN_USERNAME','u'/g' /var/www/html/memcache.php

    It gives this:

    sed -i s/ADMIN_USERNAME,memcache/ADMIN_USERNAME,u/g /var/www/html/memcache.php

    Oops! Your single quotes were not interpreted how you thought. An easy workaround for this case is to quote protect your sed command from the shell with double quotes instead.

    sed -i "s/'ADMIN_USERNAME','memcache'/'ADMIN_USERNAME','u'/g" /var/www/html/memcache.php

    0 讨论(0)
  • 2021-02-04 06:34

    You'll have to use hex escapes, for example, to do those replacements.

    $ echo "'foo'" | sed 's/\x27foo\x27/\x27bar\x27/'
    'bar'
    

    You could also use octal escapes: \o047 (that's a lower-case "Oh") or decimal escapes: \d39.

    0 讨论(0)
  • 2021-02-04 06:49

    To understand how to do this, you need to understand how the shell is messing with your quotes and backslashes as well as whether sed is messing with them.

    Generally, it is easiest in the shell to use single quotes - except when the regex must match single quotes. There are no special characters inside single quotes; the next single quote ends it. Double quotes are much harder; backslashes and dollars and back-quotes are all special.

    The first requirement is best handled by a single quoted sed expression:

    Change:

    [server]
    server[] = "localhost:11211"
    

    to

    [server]
    server[] = "localhost:11211"
    server[] = "localhost:11212"
    

    For this, we could use:

    -e '/^server\[] = "localhost:11211"/{p;s/11211/11212/}'
    

    The shell simply sends everything inside the single quotes to sed, untouched. The only special character to sed is the open square bracket. That would start a character class except for the backslash before it. The close square bracket is only special when you are in a character class, so it does not need escaping. The actions on recognizing that line are to print the original, then do the 11212 for 11211 substitution; the default print will print the modified line.

    The other part is a bit trickier. Because the patterns contain single quotes, we're going to be best off using double quotes around the regex.

    Change:

    define('ADMIN_USERNAME','username');    // Admin Username
    define('ADMIN_PASSWORD','password');    // Admin Password
    
    $MEMCACHE_SERVERS[] = 'mymemcache-server1:11211'; // add more as an array
    $MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
    

    to:

    define('ADMIN_USERNAME','myusername');  // Admin Username
    define('ADMIN_PASSWORD','mypassword');      // Admin Password
    
    $MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array
    $MEMCACHE_SERVERS[] = 'localhost:11212'; // add more as an array
    

    The first two lines might be mappable using a single regex:

    -e "s/^\(define('ADMIN_[A-Z]\{0,8\}','\)\([^']*'\)/\1my\2/"
    

    This captures two parts - the define('ADMIN_USERNAME',' as \1 and the username' as \2, and the substitution places my between the two parts. This was not too bad; the shell doesn't do anything special with any of the characters, so you just type what you want sed to see inside the double quotes.

    The last two lines are trickier:

    -e "s/^\(\$MEMCACHE_SERVERS\[] = '\)[^:]*\(:[0-9]*'\)/\1localhost\2/"
    

    However, not much trickier; you just need to stop the shell from expanding the $MEMCACHE_SERVERS as a shell variable, which is why there's a backslash in front of the $. The square bracket needs protection from sed as before. The regex matches everything that isn't a colon and replaces it with localhost.

    So, this script should work:

    sed -e '/^server\[] = "localhost:11211"/{p;s/11211/11212/}' \
        -e "s/^\(define('ADMIN_[A-Z]\{0,8\}','\)\([^']*'\)/\1my\2/" \
        -e "s/^\(\$MEMCACHE_SERVERS\[] = '\)[^:]*\(:[0-9]*'\)/\1localhost\2/" \
        "$@"
    

    However, I've only explained why it should work; I've not demonstrated that it does work!

    0 讨论(0)
  • 2021-02-04 06:50

    You can replace the single quotes in the sed command with double-quoted single quotes. The shell sees a single quote as ending a string. So, let it. You had

    sed -i 's/'ADMIN_USERNAME','memcache'/'ADMIN_USERNAME','u'/g' /var/www/html/memcache.php
    

    But, if you replace the ' in the sed command with '"'"', then shell will see the first ' as ending the first single-quoted string, then "'" as a double-quoted single quote, and then the last ' as a beginning of a new single-quoted string. That'd be

    sed -i 's/'"'"'ADMIN_USERNAME'"'"','"'"'memcache'"'"'/'"'"'ADMIN_USERNAME'"'"','"'"'u'"'"'/g' /var/www/html/memcache.php
    

    You should also be able to do '\'' in place of the ' within the command, for the same reason.

    sed -i 's/'\''ADMIN_USERNAME\'',\''memcache\''/\''ADMIN_USERNAME\'',\''u\''/g' /var/www/html/memcache.php
    

    But really, it'd be better to use an alternative mechanism. I'd suggest defining the source and target strings as variables, and then put those in the sed string.

    SRC="'ADMIN_USERNAME','memcache'"
    DST="'ADMIN_USERNAME','u'"
    sed -i "s/$SRC/$DST/g" /var/www/html/memcache.php
    

    That's way more readable, and it makes it easier for you to handle the quoting mess in a sane way with bite-sized chunks. Yay "shell variable contents aren't subject to word expansion unless you force it" knowledge. :)

    Make sure you don't put a / in the $SRC or $DST variables, though. ;)

    0 讨论(0)
提交回复
热议问题