SED command to replace test with newline break - sed

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

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.

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.

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.

Related

Getting a value of a node in an XML file

I'm trying to write a script that will get the value of a node in multiple XML files.
Here is the XML structure :
<Report>
<ReportSections>
<ReportSection>
<Body>
<ReportItems>
<Textbox Name="lbReportName">
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>Activity Report</Value>
</TextRun>
</TextRuns>
</Paragraph>
</Paragraphs>
</Textbox>
</ReportItems>
</Body>
</ReportSection>
</ReportSections>
</Report>
I use this script to search through the XML :
Select-XML -Path "N:\TEMP\XML\0.xml" –Xpath "//*[#Name='lbReportName']"
(Because the structure is not the same above the name "lbReportName").
Now, how can I get the value "Activity Report" ?
(After the name "lbReportName", the structure is the same for all XML files)
After the name "lbReportName", the structure is the same for all XML files
That makes it easier - since you already have a wildcard selector with an appropriate clause for the "common ancestor", it's as easy as just describing the rest of the path down to the <Value> nodes:
$ValueNodes = Select-Xml ... "//*[#Name='lbReportName']/Paragraphs/Paragraph/TextRuns/TextRun/Value"
# Let PowerShell's property enumeration behavior tackle the rest
$Values = $ValueNodes.Node.InnerText

NXLog Windows Events and Source

I want to upload events only from a "puppet" source, which is "Windows Logs\Application".
I guess I have to change the line <Select Path = 'Application'> * </Select>
How to filter source "puppet" in nxlog.conf?
<Input in>
Module im_msvistalog
ReadFromLast TRUE
<QueryXML>
<QueryList>
<Query Id='1'>
<Select Path='Application'>*</Select>
</Query>
</QueryList>
</QueryXML>
Exec $FileName = 'winapp.log';
Exec $EventTime = $EventReceivedTime;
</Input>
<Output out1>
Module om_udp
Host 10.10.0.40
Port 514
Exec to_syslog_bsd();
</Output>
<Route 1>
Path in => out1
</Route>
here's how i've done it :
<Query Id='1'>
<Select Path="Application">*[System[Provider[(#Name="MySrcName")]]]</Select>
</Query>
I found the tree path : System > Provider > Name
by opening the windows event viewer, then select your event, then event properties, then details.
I said that because it may be different for you based on your windows version.

TVML: adding new lines to a description text

Experimenting with Apple TV's TVML: I'm using a Product Template, and in the description field I'd like to add carriage returns, to make it look somewhat like a list.
Here is a simple example:
var Template = function() { return `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<productTemplate>
<banner>
<infoList>
</infoList>
<stack>
<title>Big Title</title>
<description>
Line one
Line two
</description>
</stack>
</banner>
</productTemplate>
</document>`
}
I've tried \n, &#xD, &#xA between the lines, and even something like this:
<![CDATA[
Line 1 <br />
Line 2 <br />
]]>
But none of these work. Is there a way to incorporate line breaks in TVML descriptions?
Having this code in a template.xml.js and loading it via the Presenter.js in the TVMLCatalog example from apple:
<stack>
<description>Insert your \n username (tipically your ID)</description>
</stack>
It renders
This also works:
var Template = function() {
const description = `
Line 1
Line 2
`.trim();
return `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<productTemplate>
<banner>
<infoList>
</infoList>
<stack>
<title>Big Title</title>
<description>
${description}
</description>
</stack>
</banner>
</productTemplate>
</document>`
}

Add attribute to xml with XML::Simple

I am trying to add an attribute to existing XML, using XML::Simple.
<arbre>
<branche name="courbe" >
<description>
<![CDATA[une belle branche]]>
</description>
<feuilles>
<fleur color="blue" order="1" />
<fleur color="white" order="2" />
<fleur color="yellow" order="3" />
</feuilles>
</branche>
<branche name="droite" >
<description>
<![CDATA[une branche commune]]>
</description>
<feuilles>
<fleur color="purple" order="1" />
<fleur color="green" order="2" />
</feuilles>
</branche>
</arbre>
That I am trying to transform into :
<arbre>
<branche name="courbe" type="conifere">
<description>
<![CDATA[une belle branche]]>
</description>
<feuilles>
<fleur color="blue" order="1" />
<fleur color="white" order="2" />
<fleur color="yellow" order="3" />
</feuilles>
</branche>
<branche name="droite" type="resineux">
<description>
<![CDATA[une branche commune]]>
</description>
<feuilles>
<fleur color="purple" order="1" />
<fleur color="green" order="2" />
</feuilles>
</branche>
</arbre>
Notice the type attribute in branche tag.
So far I have the following :
#!/usr/bin/env perl -w
use strict;
use XML::Simple;
use Data::Dumper;
my $funclist = XML::Simple->new();
my $arbres = $funclist->XMLin("test.xml");
print Dumper($arbres);
exit 0;
From what I understand from the documentation $arbres is a hash in which I have to insert in each branche key the type attribute key and value.
Exept that I have no clue at where and how ($arbres{something} = "conifere" ?).
Thanks
use strict;
use warnings FATAL => 'all';
use XML::Simple qw();
my %branche_map = (
courbe => 'conifere',
droite => 'resineux',
);
my $xs = XML::Simple->new(StrictMode => 1, ForceArray => 1, KeyAttr => undef, RootName => 'arbre');
my $arbres = $xs->XMLin('test.xml');
for my $branche (#{ $arbres->{branche} }) {
$branche->{type} = $branche_map{ $branche->{name} };
}
print $xs->XMLout($arbres)
Using XML::XSH2, a wrapper around XML::LibXML
open test.xml ;
for //branche[#name='courbe'] set #type 'conifere' ;
for //branche[#name='droite'] set #type 'resineux' ;
save :b ;

Extra xml tag when using Lib:XSLT to create text file

Cross-post http://perlmonks.org/index.pl?node_id=979710
I'm trying to create a text file from some XML using Perl and Lib::XSLT, my transformation works fine except Lib::XSLT adds an unwanted ?xml version tag to the start of the file, how can I stop it doing this?
Here's my XSLT:
<xslt:stylesheet version="1.0" xmlns:data="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/generic" xmlns:xslt="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:message="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xslt:param name="sep">|</xslt:param>
<xslt:output method="text" />
<xslt:template match="message:MessageGroup">
<xslt:for-each select="data:DataSet">
<!-- get dimensions (but not time) and store in dimensions variable -->
<xslt:for-each select="data:Series">
<xslt:variable name="dimensions">
<xslt:for-each select="data:SeriesKey">
<xslt:for-each select="data:Value">
<xslt:value-of select="#value" />
<xslt:value-of select="$sep" />
</xslt:for-each>
</xslt:for-each>
</xslt:variable>
<!--get obs statuses and store in obs statuses variable-->
<xslt:variable name="obsStatuses">
<xslt:for-each select="data:Attributes">
<xslt:for-each select="data:Value">
<xslt:value-of select="#value" />
</xslt:for-each>
</xslt:for-each>
</xslt:variable>
<!--write out dimensions variable, time, observation, obsstatuses variable-->
<xslt:for-each select="data:Obs">
<xslt:value-of select="$dimensions" />
<xslt:value-of select="data:Time" />
<xslt:value-of select="$sep" />
<xslt:value-of select="data:ObsValue/#value" />
<xslt:value-of select="$sep" />
<xslt:value-of select="data:Attributes/data:Value/#value"/>
<xslt:text>
</xslt:text>
</xslt:for-each>
</xslt:for-each>
</xslt:for-each>
</xslt:template>
</xslt:stylesheet>
Here's the Perl:
use Lib::XSLT;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source = XML::LibXML->load_xml(location => "$xmlFile");
my $style_doc = $parser->parse_file(Path::Class::File->new("$xsltFile"));
my $stylesheet = $xslt->parse_stylesheet($style_doc);
open OUTPUTFILE, ">>$outputFile" or die("Unable to open $outputFile, $!");
print OUTPUTFILE $stylesheet->transform($source);
close OUTPUTFILE;
Storing the result of $stylesheet->transform() and using $stylesheet->output_file() fixes this issue, e.g:
use Lib::XSLT;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source = XML::LibXML->load_xml(location => "$xmlFile");
my $style_doc = $parser->parse_file(Path::Class::File->new("$xsltFile"));
my $stylesheet = $xslt->parse_stylesheet($style_doc);
my $results = $stylesheet->transform($source);
$stylesheet->output_file($results, $outputFile);
Why is the <?xml> declaration unwanted? It is valid XML and and has no influence on parsers.