How to skip commented lines in sipp xml file - sipp

I am trying to grep for a pattern in sipp xml file. That pattern might be commented in that file, it should skip the commented occurence and search for next occurence. Pattern : pcap file : test.xml
As <exec play_pcap_audio="MULAW_p40_song.pcap"/> commented in xml file, it should return only <exec play_pcap_audio="MULAW_p41_song.pcap"/>
But I am getting
grep "pcap" test.xml
<exec play_pcap_audio="MULAW_p40_song.pcap"/>
<exec play_pcap_audio="MULAW_p41_song.pcap"/>
test.xml :
<!--
<nop>
<action>
<exec play_pcap_audio="MULAW_p40_song.pcap"/>
</action>
</nop>
-->
<nop>
<action>
<exec play_pcap_audio="MULAW_p41_song.pcap"/>
</action>
</nop>
Anyone can help me on this?

Related

bash/sed/awk: replace arbitrary substring in arbitrary files

Update:
I have tried markp-fuso's answer and it worked like a charm
I'm starting to get frustrated here as I'm not a daily user of bash/sed and the like.
Starting point:
I have many subfolders with many source files (.c,.cpp,.cxx).
These source files are referenced for compilation in project files (.vcxproj).
What I want to do:
I want to find all source files that contain the string #import. I then want to find all project files that reference those source files.
I then want to edit all occurences of these references inplace within these project files
e.g. <Include="folder/file.cpp"/> -> <Include="folder/file.cpp" Attribute="Value"/>
What I have tried:
egrep -lir --include=*.{c,cpp,cxx} "(#import)" ./e3 | xargs -L 1 basename | egrep -ir --include=*.vcxproj -f - ./e3 | sed 's/:/ /g'
which produces a list like that:
./src/base/base.vcxproj <ClCompile Include="Folder1\Folder1File1.cpp" />
./src/mod/mod.vcxproj <ClCompile Include="Folder2\Folder2File1.cpp" />
./src/ext/ext.vcxproj <ClCompile Include="Folder3\Folder3File1.cpp" />
So I then tried
egrep -lir --include=*.{c,cpp,cxx} "(#import)" ./e3 | xargs -L 1 basename | egrep -ir --include=*.vcxproj -f - ./e3 | sed 's/:/ /g' | awk '{ sed -iE 's/($2,$3)/\1 Attribute="Value"/g' }'
which errors out with
bash: syntax error near unexpected token `('
I have tried a solution with a shell script, which didn't work either and I don't know if and how to solve above error message. I'm open to any solution as long as it's running within bash, can even be more gross than what I came up with.
Setup:
mkdir -p src/{base,mod,ext}
echo 'some stuff on this line
<ClCompile Include="Folder1\Folder1File1.cpp" />
some more stuff on this line' > src/base/base.vcxproj
echo 'some stuff on this line
<ClCompile Include="Folder2\Folder2File1.cpp" />
some more stuff on this line' > src/mod/mod.vcxproj
echo 'some stuff on this line
<ClCompile Include="Folder3\Folder3File1.cpp" />
some more stuff on this line' > src/ext/ext.vcxproj
For the sake of getting something working in my environment I've placed the intermediate data in a local file:
$ cat proj.dat
./src/base/base.vcxproj <ClCompile Include="Folder1\Folder1File1.cpp" />
./src/mod/mod.vcxproj <ClCompile Include="Folder2\Folder2File1.cpp" />
./src/ext/ext.vcxproj <ClCompile Include="Folder3\Folder3File1.cpp" />
One idea using parameter substitution:
while read -r fname oldstring # 2nd-Nth space-delimited fields go into the single variable "oldstring"
do
oldstring="${oldstring//\\/\\\\}" # escape literal backslashes
newstring="${oldstring//\/>/ Attribute=\"Value\"\/>}" # replace /> with Attribute="Value"/>
echo "##################### ${fname}"
sed "s|${oldstring}|${newstring}|g" "${fname}"
done < proj.dat
NOTES:
sed replace applied to all occurences in a file
if additional datasets cause sed to abort with errors it may be necessary to add additional parameter expansions to escape other problematic characters
added a space on the front of the Attribute string since the textual description suggested a space may not exist before the /> (eg, ...file.cpp"/>)
OP should be able pipe the current egrep | xargs | egrep | sed to this while loop (replacing done < proj.dat with done)
once OP is satisfied with the results the -i flag can be added to the sed call to perform an inplace update of ${fname}
Generates:
##################### ./src/base/base.vcxproj
some stuff on this line
<ClCompile Include="Folder1\Folder1File1.cpp" Attribute="Value"/>
some more stuff on this line
##################### ./src/mod/mod.vcxproj
some stuff on this line
<ClCompile Include="Folder2\Folder2File1.cpp" Attribute="Value"/>
some more stuff on this line
##################### ./src/ext/ext.vcxproj
some stuff on this line
<ClCompile Include="Folder3\Folder3File1.cpp" Attribute="Value"/>
some more stuff on this line

Nested sed (sed in sed)

I want to extract the number of hostname (like Server01 => 01) and store this as a part of string into a file.
Unfortunatelly it still doesn't work. The last try looks like:
sed s/TRUNKNUMBER/0812345`hostname | sed -e 's/[^0-9]//g'`/ -i sipgate.xml
But the result is, TRUNKNUMBER is removed, instead replaced by number like '081234501'.
What did I wrong? Also I want to know how to count the chars if sed-error is given like
sed: -e expression #1, char 13: unknown option to `s'
UPDATE1:
Sure I can further informations. I thought it's clear, but sorry.
floh#Host01:~$ hostname
Host01
floh#Host01:~$ cat sipgate.xml
<extension name="sipgate">
<condition field="destination_number" expression="^TRUNKNUMBER">
...
</condition>
</extension>
</extension>
As I wrote I want to replace "TRUNKNUMBER" in sipgate.xml by a string concated by "0812345" + Number of Hostname.
With the one sed-command I can extract the number of hostname:
floh#Host01:~$ hostname | sed -e 's/[^0-9]//g'
01
This is fine, now I tried with:
sed s/TRUNKNUMBER/0812345`hostname | sed -e 's/[^0-9]//g'`/ -i sipgate.xml
Then I got:
floh#Host01:~$ cat sipgate.xml
<extension name="sipgate">
<condition field="destination_number" expression="^">
...
</condition>
</extension>
</extension>
Which is not good because I expected '...expression="^081234501">' in the second line.
Sorry, I found out the command was already ok:
sed s/TRUNKNUMBER/0812345`hostname | sed -e 's/[^0-9]//g'`/ -i sipgate.xml
The result was "wrong" because I acidentally reused the source-file which didn't had the string TRUNKNUMBER. (Obviously I forgot to restore the source-file for that try)
Now I learned that I should remove -i next time in order to test the output instead trying to modify the file.
Again I'm sorry!
Floh

How to use variable with \ in sed command

I have a strings file that I want to replace with sed command.
String in the file :
<ItemGroup>
<None Include="xyz.config" />
<BundleResource Include="Settings.txt" />
<BundleResource Include="Resources\Base.lproj\Localizable.strings" /> <BundleResource Include="Resources\Base.lproj\InfoPlist.strings" />
</ItemGroup>
Below is my command:
CONTENT="<BundleResource Include="Resources\Base.lproj\Localizable.strings" /><BundleResource Include="Resources\Base.lproj\InfoPlist.strings" /><BundleResource Include="Resources\es.lproj\Localizable.strings" /><BundleResource Include="Resources\es.lproj\Infoplist.strings" />"
sed -i "s|\.*<BundleResource Include=\"Resources\\Base.lproj\\.*|${CONTENT}|g"
but when I use above sed command, it didn't replace it. Do you have any idea why? Did I did wrong with my sed command?
My end result that I want is:
<ItemGroup>
<None Include="xyz.config" />
<BundleResource Include="Settings.txt" />
<BundleResource Include="Resources\Base.lproj\Localizable.strings" />
<BundleResource Include="Resources\Base.lproj\InfoPlist.strings" />
<BundleResource Include="Resources\es.lproj\Localizable.strings" />
<BundleResource Include="Resources\es.lproj\Infoplist.strings" />
</ItemGroup>
Thank you!
This is a problem with bash and sed backslash quoting. If you try
sed -i "s|\.*<BundleResource Include=\"Resources\\\\Base.lproj\\\\.*|${CONTENT}|g"
it should do what you want. Both bash and sed are collapsing backslahes, so you need four!
Also, you've used double quotes when assigning a string containing double quotes to CONTENT, which won't do what you want. Try single quotes:
CONTENT='<BundleResource Include="Resources\Base.lproj\Localizable.strings" /><BundleResource Include="Resources\Base.lproj\InfoPlist.strings" /><BundleResource Include="Resources\es.lproj\Localizable.strings" /><BundleResource Include="Resources\es.lproj\Infoplist.strings" />'
That said, as a commenter has pointed out, you'd be better off using a proper XML tool rather than sed.
As chepner mentioned, you should likely use an xml tool to edit this rather than SED, however, if you wanted to use SED, you likely want something like this:
sed "s|<BundleResource Include=\"Resources\\\\Base.lproj\\\\[^>]*>|${CONTENT}|g" < tmp.txt
I replaced the .*'s, as those can be problematic. The second .* is replaced with [^>]>, so it only matches/replaces until the next > character. I also double escaped the \ characters.

Move and rename files based on the folder they are in

I am trying to move some files in ANT but can't figure out how to do it. I know how to do it in a sequential way but can't figure out the "ant way" to do this.
Move the files from:
./<language>/<FileName>.properties
to:
./<FileName>_<language>.properties
So for example I have:
./fr/file1.properties
./fr/file2.properties
./fr/file3.properties
./en/file1.properties
./en/file2.properties
./en/file3.properties
./ko/file1.properties
./ko/file2.properties
./ko/file3.properties
I need to move these up one dir and rename the files like this:
./file1_fr.properties
./file2_fr.properties
./file3_fr.properties
./file1_en.properties
./file2_en.properties
./file3_en.properties
./file1_ko.properties
./file2_ko.properties
./file3_ko.properties
Is there an easy way to do this mapping in ant? I don't know what languages I will support or what the filenames may be.
In bash this would be straightforward. I would do something like this:
find ./* -maxdepth 0 -type d | while read DIR; do
# */ Correct syntax highlighting
find $DIR -maxdepth 0 -type f | while read FILE; do
# Note: this would produce file1.properties_fr
# which isn't exactly right. Probably need to
# use sed to remove and add .properties.
mv $DIR/$FILE ./$FILE_$DIR
done;
done;
Use a regular expression mapper in a move task:
<target name="rename">
<move todir=".">
<fileset dir=".">
<include name="**/*.properties" />
</fileset>
<mapper type="regexp" from="([^/]*)/([^/]*)(\.properties)$" to="\2_\1\3" />
</move>
</target>

how to substite last occurrance of pattern in large file efficently

Given a file with the following contents:
<root>
<a></a>
<b></b>
</root>
The command should output:
<root>
<a></a>
<b></b>
Things I've tried using the GNU Win32 port of sed:
Remove the last two lines.
This is fast, but it assumes </root> is the second to last line and will cause a bug if it's not.
sed -e '$d' test.xml | sed -e '$d'
Substituting all occurrences of </root> with an empty string.
This works, but is slower than the first solution, and will break if there are nested <root> elements (unlikely).
sed -e 's|</root>||' test.xml
The file I'm dealing with can be large so efficiency is important.
Is there a way to limit sed substitution to the last occurrence in the file? Or is there some other utility that would be faster?
Using Perl with File::Backwards should be very fast (relative, I know, but still...). Perlfaq5 has a topic on going through a file backwards and removing lines. You can check for your pattern using this topic's code as a starting point.
With sed:
sed -e ':a;N;$!ba;s|\(.*\)</root>\n\(.*\)|\1\2|'
How about using awk for this.
AWK:
awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' filename
First /pattern/{action} statement looks for lines with only </root>. It pattern finds it, action ignores it.
Second /pattern/{action} statement looks for lines containing </root> anywhere in the line. If pattern finds it, sub function replaces it with nothing and prints rest of the line.
Third action which is 1 is true for all the lines that does not have pattern </root> in them. If it finds it, it prints it.
I did a quick test and this was the result -
Test:
[jaypal:~/Temp] cat tmp
<root>
<a></a>
<b></b>
</root>
<root>
<a></a>
<b></b>
</root><root>
<a></a>
<b></b></root>
[jaypal:~/Temp] awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' tmp
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>
SED:
This should also work. Though it will remove all </root> and not just the last occurrence.
sed '/<\/root>/,$s///' filename
This might work for you:
sed '/<\/root>/,/<root>/{/<\/root>/{h;d};H;//{x;p};${x;s/[^\n]*\n//p};d}' file
This assumes that each <root> tag is matched with a closing </root> tag and that these tags occur on separate lines (as per the example).
Explanation:
Focus on lines between a closing </root> tag and an opening <root> tag or end-of-file.
If it is a closing </root> tag, save it in the hold space (HS) and then delete it and start a new cycle.
For all other lines within focus (see point 1) append them to the HS.
If it is and opening <root> tag, swap to the HS and print out its contents.
If it is the end-of-file i.e. between a </root> tag and last line of the file, swap to the HS, delete the first line i.e. the closing </root> tag and print the remainder.
For all lines within the focus, delete and start a new cycle.
An alternative solution with two passes:
sed -n '/<\/root>/=' file | sed -n '$s/$/d/p' | sed -f - file
Explanation:
Print out the line numbers of closing </root> tags
Generate a sed delete command from the last matched line number.
Pipe the command through to an instance of sed reading the source file.
Use time function to see which one is efficient. sed should be efficient.
$time command
In my opinion, there is nothing which is faster than grep. try it with awk index() to see if it is any faster.

Resources