xmlstarlet to insert tags - xmlstarlet

I am using the command:
xmlstarlet ed --omit-decl --subnode "/boinc" --type elem -n app -v "" project_00.xml > project_01.xml
However, I would like to insert two more tags into that one:
<app>
<name>name</name>
<nikname>nikname</nikname>
</app>
In my project_00.xml, I already have others tag app and it is causing conflicts.
The problem with this command:
xmlstarlet ed --subnode "/boinc" --type elem -n app -v "" project_00.xml| xmlstarlet ed --subnode //app --type elem -n name -v "newApp"| xmlstarlet ed --subnode //app --type elem -n user_friendly_name -v "New.App" > project_01.xml
is that it created this.:
<app name="wilson">
<name>wilson</name>
<user_friendly_name>Mr.Wilson</user_friendly_name>
<name>newApp</name>
<user_friendly_name>New.App</user_friendly_name>
</app>
<app>
<name>newApp</name>
<user_friendly_name>New.App</user_friendly_name>
</app>
Does know the exactly command?
I tried this command but it replicated the to all app tags
xmlstarlet ed -s "/boinc" -t elem -n app -v "" -s "/boinc/app" -t elem -n name -v "name" -s "/boinc/app" -t elem -n user_friendly_name -v "New.App" project_00.xml > project_01.xml

Basically, you need an XPath expression to match the node you just inserted; since --subnode always puts new children in last place you can use /boinc/app[last()]:
xmlstarlet ed \
--subnode /boinc --type elem -n app -v '' \
--subnode '/boinc/app[last()]' --type elem -n name -v newApp \
--subnode '/boinc/app[last()]' --type elem -n user_friendly_name -v New.App \
project_00.xml > project_01.xml

Related

Append to sed output [duplicate]

I'm trying to add && \ on end of each line on a text file except the last line.
Sample input:
ps
mkdir repo
cd repo/
touch file1.txt
Expected output:
ps && \
mkdir repo && \
cd repo/ && \
touch file1.txt
First attempt
I tried this, but it outputs && \ on each line including the final line:
awk '{print $0"&& \\"}' RS="\r*\n\r*"
Second attempt
I tried using sed:
sed '1s/^//;$!s/$/"&&" \\/;$s/$//'
This seems to add extra newlines:
ps
&& \
mkdir repo
&& \
cd repo/
&& \
touch file1.txt
You could use sed for something that simple:
printf "line 1\nLine 2\nLine 3\n" | sed '$ ! s/$/ \&\& \\ /'
Output
line 1 && \
Line 2 && \
Line 3

error while running a R script in a bash script

e made a bash script as follows:
#! /bin/bash
OUTDIR=".//DATA/share/pipelines/results/"
INDIR="./DATA/share/pipelines/test_data/infile/"
projectname=$1
input_bam=$2
bins=$3
mkdir OUTDIR || true
of="${OUTDIR}"
ind="${INDIR}"
./DATA/share/pipelines/script.R \
-b "${bins}" \
-c "${projectname}" \
-o "${of}" \
-i "${ind}"
echo "first step is done"
when I run the script using the following command:
bash first.sh 30 beh
I will get this error:
mkdir: cannot create directory ‘OUTDIR’: File exists
first.sh: line 17: ./DATA/share/pipelines/script.R: No such file or directory
first step is done
do you know how to solve the problem?
When you call
bash first.sh 30 beh
$1 holds 30, $2 holds beh and $3 is not defined.
input_bam ist set to $2 but is never used.
With [ ! -d ${OUTDIR} ] you should be able to test if the directory exists.
#! /bin/bash
#Please check if it should be
# relative to the current working directory (starting with './')
# or absolute (starting with '/')
BASEDIR="/DATA/share/pipelines/" #"./DATA/share/pipelines/"
OUTDIR=${BASEDIR}"results/"
INDIR=${BASEDIR}"test_data/infile/"
projectname=$1
input_bam=$2 #This is never used
bins=$3 #This is not defined when callin >bash first.sh 30 beh<
[ ! -d ${OUTDIR} ] && mkdir ${OUTDIR} #Think you would create ${OUTDIR}
of="${OUTDIR}"
ind="${INDIR}"
./DATA/share/pipelines/script.R \
-b "${bins}" \
-c "${projectname}" \
-o "${of}" \
-i "${ind}"
echo "first step is done"

XMLStarlet - Adding RSS feed items

Been using an example I found here (https://stackoverflow.com/a/14397390/3168446) and I just noticed it's not adding items correctly. The following example is actually adding items outside of the channel-tag. Anyone know the correct way to make this work?
feed.xml:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>My RSS Feed</title>
<description>This is my RSS Feed</description>
</channel>
</rss>
shell script:
#!/bin/sh
TITLE="My RSS entry"
LINK="http://example.com/entry4711"
DATE="`date`"
DESC="Good news"
GUID="http://example.com/entry4711"
xmlstarlet ed -L -a "//channel" -t elem -n item -v "" \
-s "//item[1]" -t elem -n title -v "$TITLE" \
-s "//item[1]" -t elem -n link -v "$LINK" \
-s "//item[1]" -t elem -n pubDate -v "$DATE" \
-s "//item[1]" -t elem -n description -v "$DESC" \
-s "//item[1]" -t elem -n guid -v "$GUID" \
-d "//item[position()>10]" feed.xml ;
windows command line example:
xml ed -L -a "//channel" -t elem -n item -v "" -s "//item[1]" -t elem -n title -v "My RSS entry" -s "//item[1]" -t elem -n link -v "http://example.com/entry4711" -s "//item[1]" -t elem -n pubDate -v "Sat, 26 Jul 2014 01:14:30 +0200" -s "//item[1]" -t elem -n description -v "Good news" -s "//item[1]" -t elem -n guid -v "http://example.com/entry4711" -d "//item[position()>10]" feed.xml
output feed.xml:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>My RSS Feed</title>
<description>This is my RSS Feed</description>
</channel>
<item>
<title>My RSS entry</title>
<link>http://example.com/entry4711</link>
<pubDate>Sat, 26 Jul 2014 01:14:30 +0200</pubDate>
<description>Good news</description>
<guid>http://example.com/entry4711</guid>
</item>
</rss>
As you see in the output the item is added outside of the channel-tag so the feed won't validate.
Wasn't actually that hard after a good night sleep.
The reason every item is added outside of the channel tag is because I used;
xml ed -L -a "//channel"
Solution is to use a tag in the feed-template that is not used within the items you're adding. I'm using the generator-tag.
Example feed.xml with added generator-tag:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>My RSS Feed</title>
<description>This is my RSS Feed</description>
<link>http://example.com/entry4711</link>
<generator>your-rss-generator</generator>
</channel>
</rss>
Shell script where I replace //channel with //generator:
#!/bin/sh
TITLE="My RSS entry"
LINK="http://example.com/entry4711"
DATE="`date`"
DESC="Good news"
GUID="http://example.com/entry4711"
xmlstarlet ed -L -a "//generator" -t elem -n item -v "" \
-s "//item[1]" -t elem -n title -v "$TITLE" \
-s "//item[1]" -t elem -n link -v "$LINK" \
-s "//item[1]" -t elem -n pubDate -v "$DATE" \
-s "//item[1]" -t elem -n description -v "$DESC" \
-s "//item[1]" -t elem -n guid -v "$GUID" \
-d "//item[position()>10]" feed.xml ;
Windows command line where I replace //channel with //generator:
xml ed -L -a "//generator" -t elem -n item -v "" -s "//item[1]" -t elem -n title -v "My RSS entry" -s "//item[1]" -t elem -n link -v "http://example.com/entry4711" -s "//item[1]" -t elem -n pubDate -v "Sat, 26 Jul 2014 01:14:30 +0200" -s "//item[1]" -t elem -n description -v "Good news" -s "//item[1]" -t elem -n guid -v "http://example.com/entry4711" -d "//item[position()>10]" feed.xml
This will make every new item go within the channel-tag.

How do I insert an element directly after another element with XMLStarlet?

With this example XML:
<rootnode>
<element-a />
<element-b />
<element-d />
<element-e />
</rootnode>
How do I insert element <element-c/> directly after the element <element-b/> using XMLStarlet?
xml ed -i (or --insert) will put the it before the node, xml ed -a (or --append) will put it after, so you can use either one of:
xml ed -i /rootnode/element-d -t elem -n element-c -v "" file.xml
xml ed -a /rootnode/element-b -t elem -n element-c -v "" file.xml

Selecting elements from xml file with xmlstarlet, can't quite get the syntax right

Here is the xml snippet:
$ cat short.xml
<hostnames>
<hostname name="yahoo.com" type="user"/>
<hostname name="ir1.fp.vip.sp2.yahoo.com" type="PTR"/>
</hostnames>
<hostnames>
<hostname name="Inc.com" type="user"/>
<hostname name="www.inc.com" type="PTR"/>
</hostnames>
The desired output is:
yahoo.com | ir1.fp.vip.sp2.yahoo.com
Inc.com | www.inc.com
What I have so far that is only partially working:
$ xml sel -t -m "//hostname" -v "#name" -n short.xml
I cannot seem to trap the Type= condition properly. TIA.
Two more solutions using xmlstarlet only once (no need to iterate):
xmlstr='
<root>
<hostnames>
<hostname name="yahoo.com" type="user"/>
<hostname name="ir1.fp.vip.sp2.yahoo.com" type="PTR"/>
</hostnames>
<hostnames>
<hostname name="Inc.com" type="user"/>
<hostname name="www.inc.com" type="PTR"/>
</hostnames>
</root>
'
echo "$xmlstr" | xmlstarlet sel -T -t -m "//hostnames" -m "hostname[#type='user']" -v '#name' -o " | " -b -m "hostname[#type='PTR']" -v '#name' -n
echo "$xmlstr" | xmlstarlet sel -T -t -m "//hostname" -i "#type='user'" -v '#name' -o " | " -b -i "#type='PTR'" -v '#name' -n
You need to count the hostnames with xmlstarlet el or something and then iterate with something like:
xmlstarlet sel -t -c "//hostnames[1]" short.xml | xmlstarlet sel -t -m "//hostname/#name" -v . -o ' | '
This would be a lot easier if the XML was better designed. :)
The example given in question is invalid xml.
xmlstarlet --version
1.3.1
compiled against libxml2 2.8.0, linked with 20800
compiled against libxslt 1.1.26, linked with 10126
xmlstarlet val -e short.xml
short.xml:5.1: Extra content at the end of the document
<hostnames>
^
short.xml - invalid
The idea in the answer by mitm is quite nice cure.
$ xml sel -t -m //hostnames -v "concat(hostname[1]/#name,'|',hostname[2]/#name)" -n file.xml
$ xml sel -t -m //hostnames -v hostname[1]/#name -o "|" -v hostname[2]/#name -n file.xml
$ xml sel -t -m //hostname[#type='user'] -v #name -o "|" -v following-sibling::hostname/#name -n file.xml

Resources