Patchfiles - Editing Lines, rather than Replacing? - patch

Is it possible to create a diff patchfile that will edit lines themselves, rather than replacing an entire line?
For example, I have the following line:
<foo:ListeningPortBar>3423</foo:ListeningPortBar>
and I want to change this to:
<cat:LoremIpsum>3423</cat:LoremIpsum>
That is, I want to change the text around the actual port number, but preserve the port number - I need to apply this patch across a number of files, all with different port numbers - I simply want to change the tags, keeping whatever port number is in there currently.
How can you achieve this please?
Thanks,
Victor

It doesn't really matter if a patch replaces the entire line or just characters in the line (the end result is the same, no...?), but I don't think this is a "patch" question. See below for a simpler solution using "sed".
For example, assume:
$ cat f1.txt
<xml>
<foo:ListeningPortBar>3423</foo:ListeningPortBar>
</xml>
$cat f2.txt
<xml>
<cat:LoremIpsum>3423</cat:LoremIpsum>
</xml>
Then, literally the patch would be:
$ diff -u f1.txt f2.txt
--- f1.txt 2012-07-08 03:14:39.328328048 -0700
+++ f2.txt 2012-07-08 03:14:30.618177130 -0700
## -1,3 +1,3 ##
<xml>
-<foo:ListeningPortBar>3423</foo:ListeningPortBar>
+<cat:LoremIpsum>3423</cat:LoremIpsum>
</xml>
This patch file could be used as a template, modified with correct values for all your files that need to be updated, and applied to all the files individually. That sounds like more work than necessary.
On the other hand, just use "sed":
$ sed 's/<foo:ListeningPortBar>\([0-9]*\)<\/foo:ListeningPortBar>/<cat:LoremIpsum>\1<\/cat:LoremIpsum>/' f1.txt
<xml>
<cat:LoremIpsum>3423</cat:LoremIpsum>
</xml>
Since you have XML, using xsltproc is another alternative, but again probably overkill for this simple search-and-replace task.
To use this in a script, you'd do something like (replacing "etc/etc" with the sed above):
for f in $(find dir -name "*.xml" -exec egrep 'foo:ListeningPortBar' {} \; -print)
do
sed -i.bak 's/etc/etc/g' $f
done
...and then verify that the ".bak" files are actually different than the modified files.

Related

cut function on specific line

I have a file in unix, say as below:
$ cat file
1.this is the test file.
The file have data related to recent survey.
The requirement is to cut the first two characters only from the 1st line and print rest of the content of the file as it is, I have tried the cut command for same..
$ cut -c 3- file
this is the test file.
e file have data related to recent survey.
but its removing the 2 characters from each line, is there any way to implement the cut only on the first line or any other command we can use to get the required result (also the first two characters could be anything which need to remove, not particularly 1.)
Using shell utilities head, tail and cut:
head -n1 file | cut -c3-; tail -n+2 file
or a sed one-liner:
sed '1s/..//' file

Extract Middle Substring from a given String in Unix

I have a string in different ranges :
WATSON_AJAY_AB04_DOTHING.data
WATSON_NAVNEET_CK4_DOTHING.data
WATSON_PRASHANTH_KJ56_DOTHING.data
WATSON_ABHINAV_KD323_DOTHING.data
On these above string how can I extract
AB04,CK4,KJ56,KD323
in Unix?
echo "$string" | cut -d'_' -f3
You could use sed or grep for this task. But since the string is so simple, I dont think you will need to.
One method is to use the bash 'cut' command. Below is an example directly on the BASH shell/command line:
jimm#pi$ string='WATSON_AJAY_AB04_DOTHING.data'
jimm#pi$ cut -d '_' -f 3 <<< "$string"
AB04 <-- outputs the result directly
(edit: of course Lucas' answer above is also a quick 'one-liner' that does the same thing as above - he beat me to it) :)
The cut will take an _ character as the delimiter (the -d '_' part), then display the 3rd slice of the string (the -f 3 part).
Or, if you want to output that 3rd slice from a list of content (using your list above), you can write a simple BASH script.
First, save the lines above ('WATSON...etc') into something like text.txt. Then open up your favorite text editor and type:
#!/bin/sh
cut -d '_' -f 3 < $1
Save that script to some useful name like slice.sh, and make sure it is executable with something like chmod 775 slice.sh.
Then at the command line you can execute the script against your text file, and immediately get an output of those parts of the file you want (in this case the third set of text, separated by the _ character):
$ ./slice.sh text.txt
AB04
CK4
KJ56
KD323
Hope that helps! Bear in mind that the commands above may vary a bit, depending on the flavor of *nix you are using, but it should at least point you in the right direction.

Trim a file name in Unix

I have a file with name
ROCKET_25_08:00.csv
I want to trim the name of the file to
ROCKET_25_.csv
I tried mv but mv is not what I required because there will be cases where the files may be more than one.
I want the name till the second _.
How to get that in unix.
Please advise.
There are some utilities that provide more flexible renaming. But one solution that won't use anything other but included UNIX tools (like sed) would be:
ls -d * | sed -re 's/^([^_]*_[^_]*_)(.*)(\....)$/mv -v \1\2\3 \1\3/' | bash
This will only work in one directory, it won't process subdirectories.
It's not at all clear what you are actually trying to do, but if you just want to remove text between the last underscore and the period, you can do:
f=ROCKET_25_08:00.csv
echo ${f%_*}_.csv

How to get the placeholder's value which is stored in a different file (same directory) using JSch exec

With the conditions:
I cannot use any XML parser tool as I don't have permission , read only
My xmllint version does not support xpath, and I cannot update it , read only
I dont have xmlstarlet and cannot install it
I run my script using Java JSch exec channel ( I have to run it here )
So we have 3 files in a directory.
sample.xml
values1.properties
values2.properties
The contents of the files are as follows:
Sample.xml
<block>
<name>Bob</name>
<address>USA</address>
<email>$BOB_EMAIL</email>
<phone>1234567</phone>
</block>
<block>
<name>Peter</name>
<address>France</address>
<cell>123123123</cell>
<drinks>Coke</drinks>
<car>$PETER_CAR</car>
<bike>Mountain bike</bike>
</block>
<block>
<name>George</name>
<hobby>$GEORGE_HOBBY</hobby>
<phone>$GEORGE_PHONE</phone>
</block>
values1.properties
JOE_EMAIL=joe#google.com
BOB_EMAIL=bob#hotshot.com
JACK_EMAIL=jack#jill.com
MARY_EMAIL=mary#rose.com
PETER_EMAIL=qwert1#abc.com
GEORGE_PHONE=Samsung
values2.properties
JOE_CAR=Honda
DAISY_CAR=Toyota
PETER_CAR=Mazda
TOM_CAR=Audi
BOB_CAR=Ferrari
GEORGE_HOBBY=Tennis
I use this script to get the xml block to be converted to a properties file format
NAME="Bob"
sed -n '/name>'${NAME}'/,/<\/block>/s/.*<\(.*\)>\(.*\)<.*/\1=\2/p' sample.xml
OUTPUT:
name=Bob
address=USA
email=$BOB_EMAIL
phone=1234567
How do I get the value of $BOB_EMAIL in values1.properties and values2.properties. Assuming that I do not know where it is located between the two (or probably more) properties file. Bacause it should work differently if I entered
Name=Peter
in the script, it should get
name=Peter
address=France
cell=123123123
drinks=Coke
car=$PETER_CAR
bike=Mountain bike
and the think that will be searched will be PETER_CAR
EXPECTED OUTPUT (The user only needs to input 1 Name at a time and the output expected is one set of data in properties format with the $PLACEHOLDER replaced with the value from the properties file):
User Input: Name=Bob
name=Bob
address=USA
email=bob#hotshot.com
phone=1234567
User Input: Name=Peter
name=Peter
address=France
cell=123123123
drinks=Coke
car=Mazda
bike=Mountain bike
Ultimately, the script that I need has this logic:
for every word with $
in the result of sed -n '/name>'${name}'/,/<\/block>/s/.*<(.*)>(.*)<.*/\1=\2/p' sample.xml ,
it will search for the value of that word in all of the properties file in that directory(or specified properties files),
then replace the word with $ with the value found in the properties file
PARTIALLY WORKING ANSWER:
Walter A's answer is working in cmd line (putty) but not in Jsch exec.
I keep getting an error of No value found for token 'var' .
The solution beneath will look in the properties files a lot of times, so I think there is a faster solution for the problem.
The solution beneath will get you started and with small files you might be happy with it.
# Question has a bash en ksh tag, choose the shebang line you want
# Make sure it is the first line without space or ^M after it.
#!/bin/ksh
#!/bin/bash
# Remove next line (debugging) when all is working
# set -x
for name in Bob Peter; do
sed -n '/name>'${name}'/,/<\/block>/s/.*<\(.*\)>\(.*\)<.*/\1=\2/p' sample.xml |
while IFS="\$" read line var; do
if [ -n "${var}" ]; then
echo "${line}$(grep "^${var}=" values[12].properties | cut -d= -f2-)"
else
echo "${line}"
fi
done
echo
done
EDIT: Commented two possible shebang lines, set -x and added output.
Result:
name=Bob
address=USA
email=bob#hotshot.com
phone=1234567
name=Peter
address=France
cell=123123123
drinks=Coke
car=Mazda
bike=Mountain bike
. values1.properties
. values2.properties
sed -n '/name>'${NAME}'/,/<\/block>/s/.*<\(.*\)>\(.*\)<.*/echo \1="\2"/p' sample.xml >output
. output
Dangerous, and not the way I would prefer to do it.
A sed based version:
$ temp_properties=`mktemp`
$ NAME=Bob
$ sed '/./{s/^/s|$/;s/=/|/;s/$/|g/}' values*.properties > $temp_properties
$ sed -n '/name>'${NAME}'/,/<\/block>/s/.*<\(.*\)>\(.*\)<.*/\1=\2/p' sample.xml | sed -f $temp_properties
Gives:
name=Bob
address=USA
email=bob#hotshot.com
phone=1234567
It does have issues of script injection. However, if you trust the values*.properties files & contents of NAME variable, you are good to go.

How to do a mass rename?

I need to rename files names like this
transform.php?dappName=Test&transformer=YAML&v_id=XXXXX
to just this
XXXXX.txt
How can I do it?
I understand that i need more than one mv command because they are at least 25000 files.
Easiest solution is to use "mmv"
You can write:
mmv "long_name*.txt" "short_#1.txt"
Where the "#1" is replaced by whatever is matched by the first wildcard.
Similarly #2 is replaced by the second, etc.
So you do something like
mmv "index*_type*.txt" "t#2_i#1.txt"
To rename index1_type9.txt to t9_i1.txt
mmv is not standard in many Linux distributions but is easily found on the net.
If you are using zsh you can also do this:
autoload zmv
zmv 'transform.php?dappName=Test&transformer=YAML&v_id=(*)' '$1.txt'
You write a fairly simple shell script in which the trickiest part is munging the name.
The outline of the script is easy (bash syntax here):
for i in 'transform.php?dappName=Test&transformer=YAML&v_id='*
do
mv $i <modified name>
done
Modifying the name has many options. I think the easiest is probably an awk one-liner like
`echo $i | awk -F'=' '{print $4}'`
so...
for i in 'transform.php?dappName=Test&transformer=YAML&v_id='*
do
mv $i `echo $i | awk -F'=' '{print $4}'`.txt
done
update
Okay, as pointed out below, this won't necessarily work for a large enough list of files; the * will overrun the command line length limit. So, then you use:
$ find . -name 'transform.php?dappName=Test&transformer=YAML&v_id=*' -prune -print |
while read
do
mv $reply `echo $reply | awk -F'=' '{print $4}'`.txt
done
Try the rename command
Or you could pipe the results of an ls into a perl regex.
You may use whatever you want to transform the name (perl, sed, awk, etc.). I'll use a python one-liner:
for file in 'transform.php?dappName=Test&transformer=YAML&v_id='*; do
mv $file `echo $file | python -c "print raw_input().split('=')[-1]"`.txt;
done
Here's the same script entirely in Python:
import glob, os
PATTERN="transform.php?dappName=Test&transformer=YAML&v_id=*"
for filename in glob.iglob(PATTERN):
newname = filename.split('=')[-1] + ".txt"
print filename, '==>', newname
os.rename(filename, newname)
Side note: you would have had an easier life saving the pages with the right name while grabbing them...
find -name '*v_id=*' | perl -lne'rename($_, qq($1.txt)) if /v_id=(\S+)/'
vimv lets you rename multiple files using Vim's text editing capabilities.
Entering vimv opens a Vim window which lists down all files and you can do pattern matching, visual select, etc to edit the names. After you exit Vim, the files will be renamed.
[Disclaimer: I'm the author of the tool]
I'd use ren-regexp, which is a Perl script that lets you mass-rename files very easily.
21:25:11 $ ls
transform.php?dappName=Test&transformer=YAML&v_id=12345
21:25:12 $ ren-regexp 's/transform.php.*v_id=(\d+)/$1.txt/' transform.php*
transform.php?dappName=Test&transformer=YAML&v_id=12345
1 12345.txt
21:26:33 $ ls
12345.txt
This should also work:
prfx='transform.php?dappName=Test&transformer=YAML&v_id='
ls $prfx* | sed s/$prfx// | xargs -Ipsx mv "$prfx"psx psx
this renamer command would do it:
$ renamer --regex --find 'transform.php?dappName=Test&transformer=YAML&v_id=(\w+)' --replace '$1.txt' *
Ok, you need to be able to run a windows binary for this.
But if you can run Total Commander, do this:
Select all files with *, and hit ctrl-M
In the Search field, paste "transform.php?dappName=Test&transformer=YAML&v_id="
(Leave Replace empty)
Press Start
It doesn't get much simpler than that.
You can also rename using regular expressions via this dialog, and you see a realtime preview of how your files are going to be renamed.

Resources