find the last element of a line in a file

Discussion in 'Perl Misc' started by gniagnia, Feb 20, 2007.

  1. gniagnia

    gniagnia Guest

    Hi all,

    here is an extract of the log file my perl script handles:

    2007/02/15 17:59:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/
    expl/data/out/region1/ftpuser1/fichier ftp de test.txt
    2007/02/15 18:29:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/
    expl/data/out/region1/ftpuser1/video.mpg
    2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -

    etc...

    I'd like to record the file name at the end of each line, in an array,
    in order to have :


    fichier ftp de test.txt
    video.mpg
    etc...



    So far, I manage to write this script :


    push(@ftpfilesout,$1) if ( $ftpd_line =~ m:/([^/]*\S)\s*$: ) ;
    foreach $ftpfileout (@ftpfilesout)
    {
    print "$ftpfileout";
    }



    But there is a problem when the script finds a line like this one :

    2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -

    (that is to say a line that doesnt contain any file name at the
    end...)


    How can I improve my script to bypass all lines that doesnt contains
    any file name at the end?

    I think the best way to do it is to record all element situated after
    the 9th "/"
    But I dont know how to write this...

    Thanks in advance for your help!
    gniagnia, Feb 20, 2007
    #1
    1. Advertising

  2. gniagnia

    Mirco Wahab Guest

    gniagnia wrote:
    > I'd like to record the file name at the end of each line, in an array,
    > in order to have :
    >
    > fichier ftp de test.txt
    > video.mpg
    > etc...
    >
    > So far, I manage to write this script :
    >
    > push(@ftpfilesout,$1) if ( $ftpd_line =~ m:/([^/]*\S)\s*$: ) ;
    > foreach $ftpfileout (@ftpfilesout)
    > {
    > print "$ftpfileout";
    > }
    >

    No, you don't. The above is not a real Perl script.
    try:

    ...

    my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;

    print join "\n", @ftpfilesout;
    ...



    Regards

    M.
    Mirco Wahab, Feb 20, 2007
    #2
    1. Advertising

  3. gniagnia

    -berlin.de Guest

    gniagnia <> wrote in comp.lang.perl.misc:
    > Hi all,
    >
    > here is an extract of the log file my perl script handles:
    >
    > 2007/02/15 17:59:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/
    > expl/data/out/region1/ftpuser1/fichier ftp de test.txt
    > 2007/02/15 18:29:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/
    > expl/data/out/region1/ftpuser1/video.mpg
    > 2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -
    >
    > etc...
    >
    > I'd like to record the file name at the end of each line, in an array,
    > in order to have :
    >
    >
    > fichier ftp de test.txt
    > video.mpg
    > etc...
    >
    >
    >
    > So far, I manage to write this script :
    >
    >
    > push(@ftpfilesout,$1) if ( $ftpd_line =~ m:/([^/]*\S)\s*$: ) ;
    > foreach $ftpfileout (@ftpfilesout)
    > {
    > print "$ftpfileout";
    > }
    >
    >
    >
    > But there is a problem when the script finds a line like this one :
    >
    > 2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -
    >
    > (that is to say a line that doesnt contain any file name at the
    > end...)
    >
    >
    > How can I improve my script to bypass all lines that doesnt contains
    > any file name at the end?
    >
    > I think the best way to do it is to record all element situated after
    > the 9th "/"
    > But I dont know how to write this...


    Are you sure all lines will have exactly 9 slashes before the file name?

    If so, try this:

    my @files;
    while ( <DATA> ) {
    chomp;
    push @files, m{(?:.*/){9}(.*)};
    }
    print "$_\n" for @files;

    __DATA__
    2007/02/15 17:59:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/expl/data/out/region1/ftpuser1/fichier ftp de test.txt
    2007/02/15 18:29:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/expl/data/out/region1/ftpuser1/video.mpg
    2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -

    If all file names begin with "/ftp-data/expl/data/out/region1/ftpuser1/"
    it would be safer to match that exact string instead of the last
    seven slashes.

    Anno
    -berlin.de, Feb 20, 2007
    #3
  4. gniagnia

    gniagnia Guest

    Thanks a lot for your help !
    gniagnia, Feb 20, 2007
    #4
  5. gniagnia

    -berlin.de Guest

    Mirco Wahab <> wrote in comp.lang.perl.misc:
    > gniagnia wrote:
    > > I'd like to record the file name at the end of each line, in an array,
    > > in order to have :
    > >
    > > fichier ftp de test.txt
    > > video.mpg
    > > etc...
    > >
    > > So far, I manage to write this script :
    > >
    > > push(@ftpfilesout,$1) if ( $ftpd_line =~ m:/([^/]*\S)\s*$: ) ;
    > > foreach $ftpfileout (@ftpfilesout)
    > > {
    > > print "$ftpfileout";
    > > }
    > >

    > No, you don't. The above is not a real Perl script.
    > try:
    >
    > ...
    >
    > my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;


    The "grep defined" stage isn't really necessary here. A regex will only
    return an undefined capture if the capture is in an optional part. That
    doesn't happen here.

    If the match fails altogether (in list context), it returns an empty
    list, not undef, so map does the right thing and adds nothing.

    Anno
    -berlin.de, Feb 20, 2007
    #5
  6. gniagnia

    Mirco Wahab Guest

    -berlin.de wrote:
    > Mirco Wahab <> wrote in comp.lang.perl.misc:
    >>
    >> my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;

    >
    > The "grep defined" stage isn't really necessary here. A regex will only
    > return an undefined capture if the capture is in an optional part. That
    > doesn't happen here.
    >
    > If the match fails altogether (in list context), it returns an empty
    > list, not undef, so map does the right thing and adds nothing.


    Correct.

    I was under the (wrong) impression
    a *non match situation* would inject
    undefs. It, of course, does not.

    Furthermore, the lookbehind is not necessary here
    (I tried to get a non-capturing solution first), so
    a simple: @stuff= map m|\s/.*?/([^/]+?)$|, <LOGFH>
    would (imho) catch all.


    Thanks

    Mirco
    Mirco Wahab, Feb 20, 2007
    #6
  7. gniagnia

    Xicheng Jia Guest

    On Feb 20, 7:54 am, Mirco Wahab <> wrote:
    > -berlin.de wrote:
    > > Mirco Wahab <> wrote in comp.lang.perl.misc:

    >
    > >> my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;

    >
    > > The "grep defined" stage isn't really necessary here. A regex will only
    > > return an undefined capture if the capture is in an optional part. That
    > > doesn't happen here.

    >
    > > If the match fails altogether (in list context), it returns an empty
    > > list, not undef, so map does the right thing and adds nothing.

    >
    > Correct.
    >
    > I was under the (wrong) impression
    > a *non match situation* would inject
    > undefs. It, of course, does not.
    >
    > Furthermore, the lookbehind is not necessary here
    > (I tried to get a non-capturing solution first), so
    > a simple: @stuff= map m|\s/.*?/([^/]+?)$|, <LOGFH>
    > would (imho) catch all.


    how about just removing all stuff before the last forward slash:

    @stuff = map { s|.*/||; $_ } <LOGFH>

    Regards,
    Xicheng
    Xicheng Jia, Feb 20, 2007
    #7
  8. gniagnia

    Mirco Wahab Guest

    Xicheng Jia wrote:
    > On Feb 20, 7:54 am, Mirco Wahab <> wrote:
    >> -berlin.de wrote:
    >>> Mirco Wahab <> wrote in comp.lang.perl.misc:
    >>>> my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;
    >>> The "grep defined" stage isn't really necessary here. A regex will only
    >>> return an undefined capture if the capture is in an optional part. That
    >>> doesn't happen here.
    >>> If the match fails altogether (in list context), it returns an empty
    >>> list, not undef, so map does the right thing and adds nothing.

    >> Correct.
    >>
    >> I was under the (wrong) impression
    >> a *non match situation* would inject
    >> undefs. It, of course, does not.
    >>
    >> Furthermore, the lookbehind is not necessary here
    >> (I tried to get a non-capturing solution first), so
    >> a simple: @stuff= map m|\s/.*?/([^/]+?)$|, <LOGFH>
    >> would (imho) catch all.

    >
    > how about just removing all stuff before the last forward slash:
    >
    > @stuff = map { s|.*/||; $_ } <LOGFH>


    This would return matches on both lines of the OP's data:

    2007/02/15 18:29:22 192.168.0.12 ftpuser1 STOR 436699136 /ftp-data/expl/data/out/region1/ftpuser1/video.mpg
    2007/02/15 17:46:31 192.168.0.12 ftpuser1 RETR - -

    where the alternative:

    m|\s/.*?/([^/]+?)$|

    would match only on the first line
    (it bails out if no /'es with one [:space:] in front).

    Regards

    M.
    Mirco Wahab, Feb 20, 2007
    #8
  9. gniagnia

    -berlin.de Guest

    Xicheng Jia <> wrote in comp.lang.perl.misc:
    > On Feb 20, 7:54 am, Mirco Wahab <> wrote:
    > > -berlin.de wrote:
    > > > Mirco Wahab <> wrote in comp.lang.perl.misc:

    > >
    > > >> my @ftpfilesout = grep defined, map m{(?<=\s/).*?/([^/]+?)$}, <LOGFILE>;

    > >
    > > > The "grep defined" stage isn't really necessary here. A regex will only
    > > > return an undefined capture if the capture is in an optional part. That
    > > > doesn't happen here.

    > >
    > > > If the match fails altogether (in list context), it returns an empty
    > > > list, not undef, so map does the right thing and adds nothing.

    > >
    > > Correct.
    > >
    > > I was under the (wrong) impression
    > > a *non match situation* would inject
    > > undefs. It, of course, does not.
    > >
    > > Furthermore, the lookbehind is not necessary here
    > > (I tried to get a non-capturing solution first), so
    > > a simple: @stuff= map m|\s/.*?/([^/]+?)$|, <LOGFH>
    > > would (imho) catch all.

    >
    > how about just removing all stuff before the last forward slash:
    >
    > @stuff = map { s|.*/||; $_ } <LOGFH>


    That will leave line feeds at the end of each file name. It also
    won't work on data that doesn't have a file name at the end -- that
    was at least one problem addressed in the OP.

    I am never quite happy with solutions that use s/// where
    a capture would do. Simplest tool for the job, and all that...
    There is usually a capturing equivalent, in this case

    @stuff = map m|.*/(.+)|s, @data;

    If you drop the /s, it won't capture line feeds. Of course,
    it still fails on data without a file name (or, for that matter,
    for file names without a slash).

    Anno
    -berlin.de, Feb 20, 2007
    #9
    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. desktop
    Replies:
    11
    Views:
    509
    James Kanze
    Apr 27, 2007
  2. scad
    Replies:
    23
    Views:
    1,128
    Alf P. Steinbach
    May 17, 2009
  3. suresh
    Replies:
    2
    Views:
    231
    suresh
    Oct 8, 2010
  4. Replies:
    10
    Views:
    245
    Robert Klemme
    Oct 11, 2008
  5. STD
    Replies:
    8
    Views:
    610
    Randal L. Schwartz
    Feb 26, 2012
Loading...

Share This Page