0

I'm in need of some help, I need to replace text on a page using SED command with other text but it just will not work for me.

Need to replace this one:

            <key>disableMailRecentsSyncing</key>
            <true />
            <key>allowMailDrop</key>
            <false />
            <key>PreventMove</key>
            <true />

With this one:

            <key>disableMailRecentsSyncing</key>
            <false />
            <key>allowMailDrop</key>
            <true />
            <key>PreventMove</key>
            <false />

I've tried the following but it will not work:

sed -i 's/<key>disableMailRecentsSyncing</key>                 <true />                 <key>allowMailDrop</key>                 <false />                 <key>PreventMove</key>                 <true />/<key>disableMailRecentsSyncing</key>                 <false />                 <key>allowMailDrop</key>                 <true />                 <key>PreventMove</key>                 <false />/g' input.txt
Barmar
  • 741,623
  • 53
  • 500
  • 612

3 Answers3

2

For editing XML, use an XML-aware tool. For example, negating all the listed keys can be done in XSH (a wrapper around XML::LibXML) using the following command:

rename xsh:if(self::true, "false", "true")
       (//false[preceding-sibling::key[1]="allowMailDrop"]
       | //true[preceding-sibling::key[1]="PreventMove"
                or preceding-sibling::key[1]="disableMailRecentsSyncing"]) ;

Note that I'm the current maintainer of the tool.

choroba
  • 231,213
  • 25
  • 204
  • 289
0

Assumptions:

  • data is nicely formatted as in the question (otherwise a proper XML/HTML-aware tool may be easier to use than re-inventing a parser)
  • objective is to toggle the current value (ie, true becomes false and false becomes true)
  • true/false values are all lowercase
  • true/false are always preceded by a <

With a view towards an awk solution I'd want the patterns (to search for) placed into a file as this allows for flexibility without having to hardcode the solution, eg:

$ cat key.list
disableMailRecentsSyncing
allowMailDrop
PreventMove

My input file with some additional data:

$ cat input.data
<key>disableMailRecentsSyncing</key>       # match but ...
<sometimes true />                         #    leave "true" alone

<key>disableMailRecentsSyncing</key>       # match so ...
<true />                                   #   switch to "false"
<key>allowMailDrop</key>                   # match so ...
<false />                                  #   switch to "true"
<key>PreventMove</key>                     # match so ...
<true />                                   #   switch to "false"

<key>allowMailDrop</key>                   # match but ...
<Tuesday />                                #   ignore

One awk idea:

awk '
FNR==NR    { keys[$1]; next }
           { split($0,arr,"[<>]") }
toggle     { if ( arr[2] ~ /^true/  ) gsub(/<true/, "<false")
             if ( arr[2] ~ /^false/ ) gsub(/<false/,"<true" )
             toggle=0
           }
           { if ( arr[3] in keys) toggle=1 }
1
' key.list input.data

This generates:

<key>disableMailRecentsSyncing</key>
<sometimes true />

<key>disableMailRecentsSyncing</key>
<false />
<key>allowMailDrop</key>
<true />
<key>PreventMove</key>
<false />

<key>allowMailDrop</key>
<Tuesday />

Due to the use of 2 input files OP will not be able to use (GNU) awk -i inplace so the output will need to be saved to a temp file and then copying/moving the temp file to replace the current file.

markp-fuso
  • 28,790
  • 4
  • 16
  • 36
0

This might work for you (GNU sed):

cat <<\! >matchFile
        <key>disableMailRecentsSyncing</key>
        <true />
        <key>allowMailDrop</key>
        <false />
        <key>PreventMove</key>
        <true />
!
cat <<\! >replaceFile
        <key>disableMailRecentsSyncing</key>
        <false />
        <key>allowMailDrop</key>
        <true />
        <key>PreventMove</key>
        <false />
!
cat file <(echo MATCH) matchFile <(echo REPLACE) replaceFile |
sed -Ez ':a;s/(.*)(.*MATCH\1REPLACE(.*))/\3\2/;ta;s/(.*)MATCH.*//'

To replace the original file, use:

sed -E -i 'H;1h;$!d;x;s/$/\n$(echo MATCH;cat matchFile;echo REPLACE;cat replaceFile)/
           s/.*/echo "&"/e;:a;s/(.*)(.*MATCH\1REPLACE(.*))/\3\2/;ta;s/(.*)MATCH.*/\1/' file

The solution appends a delimiter MATCH followed by the matchFile, followed by a second delimiter REPLACE followed by the replaceFile.

Then using a loop and pattern matching (involving back references) the matchFile is matched against places in the original file and replaced by the text in the replaceFile.

potong
  • 55,640
  • 6
  • 51
  • 83