Single-liner for one-line substitute?

Discussion in 'Perl Misc' started by Mike Pearson, Jun 28, 2006.

  1. Mike Pearson

    Mike Pearson Guest

    Hi -

    I know almost no Perl at all, but I occasionally use

    perl -pi -e 's/old/new/g' file

    for a global search/replace in a file. I've tried to modify this to
    change only a string on the first line of a file, leaving the string
    unchanged elsewhere in the file, but I haven't been able to find a way
    to do this. Simply removing the 'g' has no effect - it still does a
    global replace.

    Can anyone tell me how to do this?

    Thanks -

    Mike
    Mike Pearson, Jun 28, 2006
    #1
    1. Advertising

  2. Mike Pearson

    -berlin.de Guest

    Mike Pearson <> wrote in comp.lang.perl.misc:
    > Hi -
    >
    > I know almost no Perl at all, but I occasionally use
    >
    > perl -pi -e 's/old/new/g' file
    >
    > for a global search/replace in a file. I've tried to modify this to
    > change only a string on the first line of a file, leaving the string
    > unchanged elsewhere in the file, but I haven't been able to find a way
    > to do this. Simply removing the 'g' has no effect - it still does a
    > global replace.


    The match operator is applied to every line in the file. The /g
    modifier changes the behavior of each application. It does not
    work across applications.

    Here is one way:

    perl -pi -e '$. == 1 && s/old/new/g' file

    Anno
    -berlin.de, Jun 28, 2006
    #2
    1. Advertising

  3. Mike Pearson

    Paul Lalli Guest

    -berlin.de wrote:
    > Mike Pearson <> wrote in comp.lang.perl.misc:
    > > perl -pi -e 's/old/new/g' file
    > >
    > > for a global search/replace in a file. I've tried to modify this to
    > > change only a string on the first line of a file, leaving the string
    > > unchanged elsewhere in the file, but I haven't been able to find a way
    > > to do this. Simply removing the 'g' has no effect - it still does a
    > > global replace.

    >
    > The match operator is applied to every line in the file. The /g
    > modifier changes the behavior of each application. It does not
    > work across applications.
    >
    > Here is one way:
    >
    > perl -pi -e '$. == 1 && s/old/new/g' file


    That would still cause Perl to loop through the entire file, each time
    checking the value of $., even though we know it will only match the
    first time. I wonder if this might be "better":

    perl -pi -e's/old/new/g; exit;' file

    That way, regardless of the success or failure of the s///, the program
    ends after the first iteration of the implicit while(<>) loop....

    Paul Lalli
    Paul Lalli, Jun 28, 2006
    #3
  4. Mike Pearson

    Andrew Guest

    Paul Lalli wrote:
    > -berlin.de wrote:
    > > Mike Pearson <> wrote in comp.lang.perl.misc:
    > > > perl -pi -e 's/old/new/g' file
    > > >
    > > > for a global search/replace in a file. I've tried to modify this to
    > > > change only a string on the first line of a file, leaving the string
    > > > unchanged elsewhere in the file, but I haven't been able to find a way
    > > > to do this. Simply removing the 'g' has no effect - it still does a
    > > > global replace.

    > >
    > > The match operator is applied to every line in the file. The /g
    > > modifier changes the behavior of each application. It does not
    > > work across applications.
    > >
    > > Here is one way:
    > >
    > > perl -pi -e '$. == 1 && s/old/new/g' file

    >
    > That would still cause Perl to loop through the entire file, each time
    > checking the value of $., even though we know it will only match the
    > first time. I wonder if this might be "better":
    >
    > perl -pi -e's/old/new/g; exit;' file
    >
    > That way, regardless of the success or failure of the s///, the program
    > ends after the first iteration of the implicit while(<>) loop....


    Interesting, Paul, i wasn't aware of the implicit "while".

    I just tried your suggestion, however, and it mysteriously zeroed out
    the file. Using "last;" in place of "exit;" yields the same empty-file
    result.

    I intuit you're on the right track (if, indeed, a "while (<>)" is
    implied), but perhaps there needs to be an additional explicit command
    preceding "exit" or "last", which forces the modified data to be
    written back to the file?

    andrew
    Andrew, Jun 28, 2006
    #4
  5. Mike Pearson

    Paul Lalli Guest

    Andrew wrote:
    > Paul Lalli wrote:
    > > -berlin.de wrote:
    > > > Mike Pearson <> wrote in comp.lang.perl.misc:
    > > > > perl -pi -e 's/old/new/g' file
    > > > >
    > > > > for a global search/replace in a file. I've tried to modify this to
    > > > > change only a string on the first line of a file, leaving the string
    > > > > unchanged elsewhere in the file, but I haven't been able to find a way
    > > > > to do this. Simply removing the 'g' has no effect - it still does a
    > > > > global replace.
    > > >
    > > > The match operator is applied to every line in the file. The /g
    > > > modifier changes the behavior of each application. It does not
    > > > work across applications.
    > > >
    > > > Here is one way:
    > > >
    > > > perl -pi -e '$. == 1 && s/old/new/g' file

    > >
    > > That would still cause Perl to loop through the entire file, each time
    > > checking the value of $., even though we know it will only match the
    > > first time. I wonder if this might be "better":
    > >
    > > perl -pi -e's/old/new/g; exit;' file
    > >
    > > That way, regardless of the success or failure of the s///, the program
    > > ends after the first iteration of the implicit while(<>) loop....

    >
    > Interesting, Paul, i wasn't aware of the implicit "while".


    Take a look at perldoc perlrun, for the -p and -n options.

    > I just tried your suggestion, however, and it mysteriously zeroed out
    > the file. Using "last;" in place of "exit;" yields the same empty-file
    > result.


    WHOOPS! You are absolutely right. I was completely forgetting how
    the -p and -i options work, in that they print each line to the
    newly-modified file right after that line has been read (and possibly
    modified by the -e'' code). Definately cannot put an exit or last
    there.

    Profuse apologies to the OP and to Anno, for my erroneous "correction".

    Paul Lalli
    Paul Lalli, Jun 28, 2006
    #5
  6. Mike Pearson

    Mike Pearson Guest

    On 28 Jun 2006 09:54:51 GMT, -berlin.de wrote:


    > perl -pi -e '$. == 1 && s/old/new/g' file


    Many thanks - that's done the job.

    Mike
    Mike Pearson, Jun 28, 2006
    #6
  7. Mike Pearson

    Andrew Guest

    Mike Pearson wrote:
    > On 28 Jun 2006 09:54:51 GMT, -berlin.de wrote:
    >
    >
    > > perl -pi -e '$. == 1 && s/old/new/g' file

    >
    > Many thanks - that's done the job.


    Two variations on the above -- one equivalent, the other for a
    different need:
    (not sure about the syntax esthetics nor efficiency of these; just
    throwing them on the table):

    This seems equivalent to the above (modifies ONLY the first line):

    perl -pi -e 'next if $done; s/old/new/g; $done++;' file

    This one modifies the first match, regardless on which line, and does
    nothing beyond that first line:

    perl -pi -e 'next if $done; s/old/new/g && $done++;' file

    (The latter also lets you re-run the command and alter the subsequent
    matching line (provided your particular regex does not match the same
    line altered in the previous match) -- a sort of "incremental" way of
    doing the standard "perl -pi -e" thing... can't think of a practical
    use, but, heck...)

    Andrew
    Andrew, Jun 28, 2006
    #7
  8. Mike Pearson

    Andrew Guest

    Bad choice of words on my part. Correcting, just to be sure:

    > This one modifies the first match, regardless on which line, and does
    > nothing beyond that first line:


    I should have said, "this one modifies the first line that matches, but
    not any subsequent lines that may also match"

    perl -pi -e 'next if $done; s/old/new/g && $done++;' file
    Andrew, Jun 28, 2006
    #8
  9. -berlin.de wrote:
    > Mike Pearson <> wrote in comp.lang.perl.misc:
    >>
    >>I know almost no Perl at all, but I occasionally use
    >>
    >>perl -pi -e 's/old/new/g' file
    >>
    >>for a global search/replace in a file. I've tried to modify this to
    >>change only a string on the first line of a file, leaving the string
    >>unchanged elsewhere in the file, but I haven't been able to find a way
    >>to do this. Simply removing the 'g' has no effect - it still does a
    >>global replace.

    >
    > The match operator is applied to every line in the file. The /g
    > modifier changes the behavior of each application. It does not
    > work across applications.
    >
    > Here is one way:
    >
    > perl -pi -e '$. == 1 && s/old/new/g' file


    Also for that to work on multiple files you need to close the filehandle:

    perl -pi -e'close ARGV if eof; $. == 1 && s/old/new/g' file*



    John
    --
    use Perl;
    program
    fulfillment
    John W. Krahn, Jun 28, 2006
    #9
  10. Mike Pearson

    Andrew Guest

    John W. Krahn wrote:
    > -berlin.de wrote:
    > > Mike Pearson <> wrote in comp.lang.perl.misc:
    > >>
    > >>I know almost no Perl at all, but I occasionally use
    > >>
    > >>perl -pi -e 's/old/new/g' file
    > >>
    > >>for a global search/replace in a file. I've tried to modify this to
    > >>change only a string on the first line of a file, leaving the string
    > >>unchanged elsewhere in the file, but I haven't been able to find a way
    > >>to do this. Simply removing the 'g' has no effect - it still does a
    > >>global replace.

    > >
    > > The match operator is applied to every line in the file. The /g
    > > modifier changes the behavior of each application. It does not
    > > work across applications.
    > >
    > > Here is one way:
    > >
    > > perl -pi -e '$. == 1 && s/old/new/g' file

    >
    > Also for that to work on multiple files you need to close the filehandle:
    >
    > perl -pi -e'close ARGV if eof; $. == 1 && s/old/new/g' file*
    >


    Well, heck -- same goes for my version; thanks for catching this:

    perl -pi -e '$done=0 if eof; next if $done; s/old/new/g &&
    $done++;' file

    Also, an afterthought I had earlier: One can, of course, generalize
    things further, to alter not just the first matching line, but, say,
    the first 3 matching lines (and nothing else):

    perl -pi -e '$count=0 if eof; next if ($count>2); s/old/new/g &&
    $count++;' file1 file2 ...

    (or, obviously, one can shift the subset down with something like "...
    next unless ( (($count>4) && ($count<10))" , and so on, and so forth,
    with any numeric comparison ("unless ($count==15)", to change only the
    16th matching line) )

    And, of course, reverting back to the original OP's task of replacing a
    particular line, regardless of whether it matches, one can similarly
    replace or try to replace any specific line or lines other than the
    first one. (Separate "s/old/new/g && $count++;" into two independent
    commands, in the above);

    andrew
    Andrew, Jun 28, 2006
    #10
  11. Mike Pearson

    Xicheng Jia Guest

    Mike Pearson wrote:
    > Hi -
    >
    > I know almost no Perl at all, but I occasionally use
    >
    > perl -pi -e 's/old/new/g' file
    >
    > for a global search/replace in a file. I've tried to modify this to
    > change only a string on the first line of a file, leaving the string
    > unchanged elsewhere in the file, but I haven't been able to find a way
    > to do this. Simply removing the 'g' has no effect - it still does a
    > global replace.
    >
    > Can anyone tell me how to do this?
    >


    For multiple files, replace only in the first line:

    perl -0777pe 's/^(.*?)old/$1new/' file*

    Xicheng
    Xicheng Jia, Jun 28, 2006
    #11
  12. Xicheng Jia wrote:
    > Mike Pearson wrote:
    >>
    >>I know almost no Perl at all, but I occasionally use
    >>
    >>perl -pi -e 's/old/new/g' file
    >>
    >>for a global search/replace in a file. I've tried to modify this to
    >>change only a string on the first line of a file, leaving the string
    >>unchanged elsewhere in the file, but I haven't been able to find a way
    >>to do this. Simply removing the 'g' has no effect - it still does a
    >>global replace.
    >>
    >>Can anyone tell me how to do this?
    >>

    >
    > For multiple files, replace only in the first line:
    >
    > perl -0777pe 's/^(.*?)old/$1new/' file*


    But that only substitutes one 'old' for one 'new' while the original does it
    for all 'old's in the line.


    John
    --
    use Perl;
    program
    fulfillment
    John W. Krahn, Jun 28, 2006
    #12
  13. Mike Pearson

    Xicheng Jia Guest

    John W. Krahn wrote:
    > Xicheng Jia wrote:
    > > Mike Pearson wrote:
    > >>
    > >>I know almost no Perl at all, but I occasionally use
    > >>
    > >>perl -pi -e 's/old/new/g' file
    > >>
    > >>for a global search/replace in a file. I've tried to modify this to
    > >>change only a string on the first line of a file, leaving the string
    > >>unchanged elsewhere in the file, but I haven't been able to find a way
    > >>to do this. Simply removing the 'g' has no effect - it still does a
    > >>global replace.
    > >>
    > >>Can anyone tell me how to do this?
    > >>

    > >
    > > For multiple files, replace only in the first line:
    > >
    > > perl -0777pe 's/^(.*?)old/$1new/' file*

    >
    > But that only substitutes one 'old' for one 'new' while the original does it
    > for all 'old's in the line.
    >


    then:

    perl -i -0777pe 's/(?:\G|^)(.*?)old/$1new/g' file*

    Xicheng :)
    Xicheng Jia, Jun 28, 2006
    #13
  14. Mike Pearson

    Xicheng Jia Guest

    Xicheng Jia wrote:
    > John W. Krahn wrote:
    >
    > perl -i -0777pe 's/(?:\G|^)(.*?)old/$1new/g' file*


    Just make a note: the start of line anchor ^ is redundant in this
    pattern when the anchor \G shows up. so it should be:

    perl -i -0777pe 's/\G(.*?)old/$1new/g' file*

    Xicheng
    Xicheng Jia, Jun 30, 2006
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alain Helfenstein

    Ruby Command Line One Liner

    Alain Helfenstein, Jan 1, 2009, in forum: Ruby
    Replies:
    5
    Views:
    129
    Alain Helfenstein
    Jan 11, 2009
  2. Larry
    Replies:
    1
    Views:
    90
    Martien Verbruggen
    Feb 3, 2005
  3. Oxnard
    Replies:
    13
    Views:
    203
    Fabian Pilkowski
    Jun 15, 2005
  4. kevin kitenik
    Replies:
    9
    Views:
    186
    Uri Guttman
    Dec 18, 2008
  5. boman
    Replies:
    13
    Views:
    255
    boman
    Aug 27, 2009
Loading...

Share This Page