CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Problem With A One-Liner Perl Script

 



matteoguglielmi
Novice

Mar 26, 2013, 5:28 AM

Post #1 of 15 (1352 views)
Problem With A One-Liner Perl Script Can't Post

Source this set of bash functions:


Code
printToStdErr() 
{
[ "$1" ] || {
printToStdErr "USAGE: $FUNCNAME MESSAGE"
return 0
}

printf "%s\n" "$*" >&2
}

assertVarValidName()
{
[ "$1" ] || {
printToStdErr "USAGE: $FUNCNAME VARNAME"
return 0
}

case $1 in
[!a-zA-Z_]*|*[!a-zA-z0-9_]*) return 1 ;;
esac
}

assertVarIsSet()
{
[ "$1" ] || {
printToStdErr "USAGE: $FUNCNAME VARNAME"
return 0
}

assertVarValidName "$1" && [ "${!1+X}" == X ]
}

replaceFirstMatchingLine() {
case $# in
0|1|2)
echo
echo "USAGE: $FUNCNAME PERLPATTERN PERLREPLACEMENT INPUTFILE1 ... [INPUTFILEN]"
echo
;;
*)
local pattern="$1" && shift
local replacement="$1" && shift
local oPTS

assertVarIsSet iNLINE && oPTS='-i'

perl $oPTS -n -e '!$t{$ARGV}
and @m = ( /'"$pattern"'/g )
and $p = join ").*(", @m
and s/.*($p).*\n*/'"$replacement"'/
and $t{$ARGV}++;
print "$_"' "$@"
;;
esac
}


Lets now suppose we have two text files called txt1:


Code
   dsgsdgs 
nnn ddd l
dsgsdgs
nnn gggg a


and txt2:


Code
   dsgsdgs 
nnnddd
dsgsdgs
dsgsdgs
dsgsdgs
dsgsdgs
nnn eee f
dsgsdgs
nnn 222 f


Now try this out yourself and see how powerful this function can be:

replaceFirstMatchingLine $'(n+)\s+' $'$1#\n' txt?

replaceFirstMatchingLine $'(n+)\s+(\w+)' $'$1#$2#\n' txt?

replaceFirstMatchingLine $'(n+)\s+(\w+)(\s+\w)' $'$1#$2#$3#\n' txt?

If you wanna make it "inline", just run it this way:

iNLINE= replaceFirstMatchingLine $....

As you see this one-liner works on multiple files and on the first matching line only.

The problem arises when the pattern we try to match contains any of the "dirty dozen" characters such as ()\ etc...

This will screw up the pattern variable $p because the dirty dozen is NOT escaped first.

This is exactly what I'm not able to add to the perl one-liner function.

I'd need something like this basically:


Code
perl $oPTS -n -e '!$t{$ARGV} 
and @m = ( /'"$pattern"'/g )
and foreach $a (@m) { push @p, quotemeta $a }
and $p = join ").*(", @p
and s/.*($p).*\n*/'"$replacement"'/
and $t{$ARGV}++;
print "$_"' "$@"


which does not work though.


Do you know why it's wrong and how to get it working the way I want to?


Thanks a lot for your help!

Best,

--matt


Laurent_R
Veteran / Moderator

Mar 26, 2013, 12:42 PM

Post #2 of 15 (1343 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post

I do not have time to read in detail your long post right now, but it seems to me that you should have a look at Perl's quotemeta function, which is probably doing what you're looking for.

http://perldoc.perl.org/functions/quotemeta.html

Also look at the '\Q ... \E' regex options.


(This post was edited by Laurent_R on Mar 26, 2013, 12:45 PM)


matteoguglielmi
Novice

Mar 26, 2013, 3:47 PM

Post #3 of 15 (1332 views)
Re: [Laurent_R] Problem With A One-Liner Perl Script [In reply to] Can't Post

That's exactly what I'm trying to do (see my initial post) but the problem is that I have to escape/quotemeta an array of patterns... how to do this in one line? (see proposed *wrong* solution)


matteoguglielmi
Novice

Mar 26, 2013, 4:13 PM

Post #4 of 15 (1329 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post

I think I made it (thanks Laurent!), now testing:


Code
perl $oPTS -n -e '!$t{$ARGV} 
and @m = ( /'"$pattern"'/g )
and $p = join "\E).*(\Q", @m
and s/.*(\Q$p\E).*\n*/'"$replacement"'/
and $t{$ARGV}++;
print "$_"' "$@"



rovf
Veteran

Mar 27, 2013, 2:47 AM

Post #5 of 15 (1322 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post


Quote
the problem is that I have to escape/quotemeta an array of patterns



Code
@m = map {quotemeta} (/..../q);



matteoguglielmi
Novice

Mar 27, 2013, 6:46 AM

Post #6 of 15 (1319 views)
Re: [rovf] Problem With A One-Liner Perl Script [In reply to] Can't Post

Oh yes, this version works too and it's more elegant:


Code
perl $oPTS -n -e '!$t{$ARGV} 
and @m = map {quotemeta} ( /'"$pattern"'/g )
and $p = join ").*(", @m
and s/.*($p).*\n*/'"$replacement"'/
and $t{$ARGV}++;
print "$_"' "$@"


Thanks a lot!


rovf
Veteran

Mar 27, 2013, 8:32 AM

Post #7 of 15 (1314 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post

Of course you could also get rid of @m:


Code
.... 
and $p = join ").*(", map {quotemeta} ( /'"$pattern"'/g )
....



matteoguglielmi
Novice

Mar 28, 2013, 7:05 AM

Post #8 of 15 (1298 views)
Re: [rovf] Problem With A One-Liner Perl Script [In reply to] Can't Post

I have another one-liner which is supposed to simply append a line to a file.

The code here below just does it if and only if the file(s) do exist and is/are not empty.

How may I modify it then so that:

1) the one-liner works with empty files,

2) the one-liner creates the files if it they do not exist (I'd prefer perl to create the files, not bash).


Code
appendLine() { 
case $# in
0|1)
echo
echo "USAGE: $FUNCNAME PERLREPLACEMENT INPUTFILE1 ... [INPUTFILEN]"
echo
;;
*)
local replacement="$1" && shift
local oPTS

assertVarIsSet iNLINE && oPTS='-i'
perl $oPTS -n -e 'if (eof) { print "$_"; s/.*\n*/'"$replacement"'/; print "$_";} else { print "$_";}' "$@"
;;
esac
}



rovf
Veteran

Mar 28, 2013, 7:50 AM

Post #9 of 15 (1293 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post

Do I miss something, or is it that your program doesn't do anything more than just appending a line containing the text PERLREPLACEMENT to the end of a file????? Well, there are corner cases (i.e. if PERLREPLACEMENT contains a $&), where the effect would be a little bit differences, but I somehow suspect that you didn't have those in mind.

Can you give me one (non-trivial) example, where you current solution is doing what it is supposed to do, so that I get a better understanding?

BTW, you will, in general, get more responses to such a topic, if you start a new thread when having a new problem, instead of re-using an existing one.


matteoguglielmi
Novice

Mar 28, 2013, 8:29 AM

Post #10 of 15 (1285 views)
Re: [rovf] Problem With A One-Liner Perl Script [In reply to] Can't Post

Ok, will open a new thread next time.

Here is an example where it works:


Code
$ echo a > txt1 

$ echo b > txt2

$ echo c >> txt2

$ appendLine $'whatever\n' txt?
a
whatever
b
c
whatever


Here is when it does not work:


Code
$ touch file1 

$ touch file2

$ appendLine $'whatever\n' file?
(no output)


So, what I'd like to have is:

1) file1 and file2 do not remain empty,

1) no need to touch/create them before calling appendLine (perl should create them instead when they are not present).


rovf
Veteran

Mar 28, 2013, 12:51 PM

Post #11 of 15 (1276 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post

That's what I suspected: You just append a string to each file (if you supply the option -i, the file will be edited in place).

Why do you need Perl for this? In bash, you can easily do a




Code
echo whatever >> your_file_goes_here


to achieve the same effect, and this would automatically cope with empty and non-existence files.

Generalizing this to an array of filenames is left as an exercise ;-)


matteoguglielmi
Novice

Mar 29, 2013, 4:25 PM

Post #12 of 15 (1258 views)
Re: [rovf] Problem With A One-Liner Perl Script [In reply to] Can't Post

Prepending the perl one-liner bash function with iNLINE=


Code
iNLINE= appendLine $'whatever\n' txt1 txt2


does not actually work for if the files do not exist a-priori, perl seems not to be able to create them out of the blue.

This can be easily fixed:


Code
appendLine() { 
case $# in
0|1)
echo
echo "USAGE: $FUNCNAME PERLREPLACEMENT INPUTFILE1 ... [INPUTFILEN]"
echo
;;
*)
local replacement="$1" && shift
local oPTS file

for file in "$@"; do
assertFileExists "$file" || > "$file"
done

assertVarIsSet iNLINE && oPTS='-i'

perl $oPTS -n -e 'if (eof) { print "$_"; s/.*\n*/'"$replacement"'/; print "$_";} else { print "$_";}' "$@"
;;
esac
}


but still, perl is not appending any line to them when they're empty.

Yes, do this in bash it's very easy, but I'd like to learn if perl can do it as well.


Kenosis
User

Mar 29, 2013, 6:07 PM

Post #13 of 15 (1255 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post


Quote
... if the files do not exist a-priori ...


The problematic (not programmatic) issue of files not existing a-priori will be effectively handled by Perl 7's ontological and phenomenological feature set, which are in beta now. Wink

touch esse_est_percipi

George Berkeley would be proud...


matteoguglielmi
Novice

Mar 29, 2013, 8:05 PM

Post #14 of 15 (1250 views)
Re: [Kenosis] Problem With A One-Liner Perl Script [In reply to] Can't Post

"verum ipsum factum"

--Giovan Battista Vico

Looking forward to perl-7 then!


Kenosis
User

Mar 29, 2013, 8:44 PM

Post #15 of 15 (1247 views)
Re: [matteoguglielmi] Problem With A One-Liner Perl Script [In reply to] Can't Post


Quote
"verum ipsum factum"


Thank you for this! Am not familiar with Vico, but it's evident that his dictum is a precursor to constructivists' epistemology, which underpins a pedagogy that's now popular in many higher-ed institutions.

Well done!

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives