1825578 Members
2268 Online
109682 Solutions
New Discussion

Another way to do...

 
Jose Mosquera
Honored Contributor

Another way to do...

Hi pals,

At present I'm using the following script sintax to search in quit mode a key-word, then in the line where this key-word be, I need replace a specific string:

ed $FILE <<-! > /dev/null
/$KEYWORD
s/$CURRENT_STRING/$NEW_STRING/
w
q
!

This works fine, but any other secure/one-shot way to do this?

Pls forget Perl.

Rgds.
14 REPLIES 14
Jean-Luc Oudart
Honored Contributor

Re: Another way to do...

awk '{if(index($0,"") > 0) sub("",""); print;}' $FILE

this will change each single line that match your search criteria

Regards,
Jean-Luc
fiat lux
G. Vrijhoeven
Honored Contributor

Re: Another way to do...

Hi,

cat $file | sed 's%$KEYWORD%$CURRENT_STRING/$NEW_STRING%g/' > $file.new
mv $file.new $file

HTH,

Gideon
Nicolas Dumeige
Esteemed Contributor

Re: Another way to do...

Jean-Luc,

Just a thing, it work perfectly if I use nawk instead of awk (sub is only available with nawk on some platform).

Cheers

Nicolas
All different, all Unix
Jean-Luc Oudart
Honored Contributor

Re: Another way to do...

I think awk in HPUX is in fact the new awk (nawk).

regards,
Jean-Luc
fiat lux
Elmar P. Kolkman
Honored Contributor

Re: Another way to do...

nawk is for SUN. Since this is a HP-UX forum, and no OS is mentioned in the question, a HP-UX specific solutions should be fine.

As for the solution:
awk -v cs="$CURRENT_STRING" -v ns="$NEW_STRING" "/$KEYWORD/ {sub(cs,ns)}
{print}" $file

Between the two '/' characters no variable replacement is done, so that's why I used double instead of single quotes. Now $KEYWORD is replaced by the shell. Only problem: $KEYWORD should NOT contain slashes ("/").

Difference between this and your script: the awk solutions (and sed) will replace, on all lines containing $KEYWORD, the first occorrence of $CURRENT_STRING, while your ed solution will only replace the first occurrence in the file.
Every problem has at least one solution. Only some solutions are harder to find.
Jose Mosquera
Honored Contributor

Re: Another way to do...

Many thanks about your sooner answers.
Suggestion from Elmar works fine but the $FILE update isn't on-line, I need a solution that replace and write into te same $FILE.

About the $KEYWORD occurrence, never mind about; is unique!

I'm looking for a live update of $FILE

Rgds.
H.Merijn Brand (procura
Honored Contributor

Re: Another way to do...

You explicitly rejected perl, which can do in-file substitutions with

# perl -pi -e'....'

Why reject it? It's not neccesarily obfuscated. I can also write readable perl :]

Enjoy, Have FUN! H.Merijn
Enjoy, Have FUN! H.Merijn
Jean-Luc Oudart
Honored Contributor

Re: Another way to do...

If you require chnage in place and do no want to use ed yopu can use "vi"
create the command file "mycom" :
/KEYWORD
:s/CURRENT_STRING/NEW_STRING/
:wq

run the command :
vi $FILE/dev/null

Regards,
Jean-Luc

fiat lux
Jose Mosquera
Honored Contributor

Re: Another way to do...

Obfuscated? ...never!
Please indicate me your suggestion in Perl

Remember, must be a live update of $FILE!

BRgds
H.Merijn Brand (procura
Honored Contributor

Re: Another way to do...

# perl -pi -e'm/$ENV{KEYWORD}/o and s/$ENV{CURRENT_STRING}/$ENV{NEW_STRING}/o' $FILE

that's all. This changes $CURRENT_STRING to $NEW_STRING only ONCE in any line that matches $KEYWORD

If you want to play more safe, and check that the patters are on a word bound, use

# perl -pi -e'm/\b$ENV{KEYWORD}\b/o and s/\b$ENV{CURRENT_STRING}\b/$ENV{NEW_STRING}/o' $FILE

and if you want to change all occurances in the matching lines

# perl -pi -e'm/\b$ENV{KEYWORD}\b/o and s/\b$ENV{CURRENT_STRING}\b/$ENV{NEW_STRING}/og' $FILE

And if you want special characters in $KEYWORD and $CURRENT_STRING to match literally,

# perl -pi -e'm/\b\Q$ENV{KEYWORD}\E\b/o and s/\b\Q$ENV{CURRENT_STRING}\E\b/$ENV{NEW_STRING}/og' $FILE

Enjoy, Have FUN! H.Merijn
Enjoy, Have FUN! H.Merijn
Tim D Fulford
Honored Contributor

Re: Another way to do...

IMHO perl is the tool of choice.

Tim
-
Nicolas Dumeige
Esteemed Contributor

Re: Another way to do...

Jose-Maria

You could also call awk from vi

ex - filename << EOF
1,$!awk '{ ... }'
wq
EOF

Cheers

Nicolas
All different, all Unix
Rodney Hills
Honored Contributor

Re: Another way to do...

Your request to update directly to the file is impossible. Only a hex-editor can do direct writes to a file and do inplace changing of data.

Any sequential file updating requires a copying of the file to a temp file, removal of the original, and renaming the temp to the original name.

The reason of course is that any changes to a sequential text file requires possible shifting of the data.

perl can make this process invisible, but it still will use the copy and rename process.

my 2 cents...

-- Rod Hills
There be dragons...
Hein van den Heuvel
Honored Contributor

Re: Another way to do...

fwiw... All of the above solution are somewhat insecure and not one-shot. For example, they will run into trouble if the file is actively being appended.

Just verify with ls -i
All the above solution will result in a new inode being used for the file.

Direct update is possible, but is tricky and has rules to follow. Really, you can only reasonably do it if the replacement string is equal in size to the to be replaced string or if there is some slop (spaces) to play with. For sake of the argument fine below an in-place solution. Oh... it's in perl. Oops. (tested, but adatted for readability but not retested)

Hein.

usage: perl update.pl your-file

#--- update.pl ----
$file = shift or die "please provide filename";
open(X,"+<$file") or die "failed to open $file";
$current=tell(X) # "=0". could leave this out.
while () {
$next=tell(X);
if (/TEST/) {
seek(X,$current,0);
s/TEST/XXXX/;
print X;
seek(X,$next,0);
}
$current=$next;
}


----
So the script remembers the (byte)address for the next line to be read, then if the line is an update candidate then reseek to the start of the current line, write, and seek back to where we interrupted.

If you know only one update is needed then you can drop the re-positions the the loop will become something like:

while () {
if (/KEYWORD/) {
seek(X,$last,0);
s/CURRENT/NEW/;
print X;
last;
}
$current=tell(X);
}

I'll leave making KEYWORD, CURRENT and NEW shell variables as an exercise to the reader :-). hint: shift from argv or read from ENV array