Re: converting a sed / grep / awk / . . . bash pipe line into python

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Peter Otten

    #1

    Re: converting a sed / grep / awk / . . . bash pipe line into python

    hofer wrote:
    Something I have to do very often is filtering / transforming line
    based file contents and storing the result in an array or a
    dictionary.
    >
    Very often the functionallity exists already in form of a shell script
    with sed / awk / grep , . . .
    and I would like to have the same implementation in my script
    >
    What's a compact, efficient (no intermediate arrays generated /
    regexps compiled only once) way in python
    for such kind of 'pipe line'
    >
    Example 1 (in bash): (annotated with comment (thus not working) if
    copied / pasted
    cat file \ ### read from file
    | sed 's/\.\..*//' \ ### remove '//' comments
    | sed 's/#.*//' \ ### remove '#' comments
    | grep -v '^\s*$' \ ### get rid of empty lines
    | awk '{ print $1 + $2 " " $2 }' \ ### knowing, that all remaining
    lines contain always at least
    \ ### two integers calculate
    sum and 'keep' second number
    | grep '^42 ' ### keep lines for which sum is 42
    | awk '{ print $2 }' ### print number
    thanks in advance for any suggestions of how to code this (keeping the
    comments)
    for line in open("file"): # read from file
    try:
    a, b = map(int, line.split(None , 2)[:2]) # remove extra columns,
    # convert to integer
    except ValueError:
    pass # remove comments, get rid of empty lines,
    # skip lines with less than two integers
    else:
    # line did start with two integers
    if a + b == 42: # keep lines for which the sum is 42
    print b # print number

    The hard part was keeping the comments ;)

    Without them it looks better:

    import sys
    for line in sys.stdin:
    try:
    a, b = map(int, line.split(None , 2)[:2])
    except ValueError:
    pass
    else:
    if a + b == 42:
    print b

    Peter
  • Roy Smith

    #2
    Re: converting a sed / grep / awk / . . . bash pipe line into python

    In article <g9ldi5$2ea$03$ 1@news.t-online.com>,
    Peter Otten <__peter__@web. dewrote:
    Without them it looks better:
    >
    import sys
    for line in sys.stdin:
    try:
    a, b = map(int, line.split(None , 2)[:2])
    except ValueError:
    pass
    else:
    if a + b == 42:
    print b
    I'm philosophically opposed to one-liners like:
    a, b = map(int, line.split(None , 2)[:2])
    because they're difficult to understand at a glance. You need to visually
    parse it and work your way out from the inside to figure out what's going
    on. Better to keep it longer and simpler.

    Now that I've got my head around it, I realized there's no reason to make
    the split part so complicated. No reason to limit how many splits get done
    if you're explicitly going to slice the first two. And since you don't
    need to supply the second argument, the first one can be defaulted as well.
    So, you immediately get down to:
    a, b = map(int, line.split()[:2])
    which isn't too bad. I might take it one step further, however, and do:
    fields = line.split()[:2]
    a, b = map(int, fields)
    in fact, I might even get rid of the very generic, but conceptually
    overkill, use of map() and just write:
    a, b = line.split()[:2]
    a = int(a)
    b = int(b)

    Comment

    • Peter Otten

      #3
      Re: converting a sed / grep / awk / . . . bash pipe line into python

      Roy Smith wrote:
      In article <g9ldi5$2ea$03$ 1@news.t-online.com>,
      Peter Otten <__peter__@web. dewrote:
      >
      >Without them it looks better:
      >>
      >import sys
      >for line in sys.stdin:
      > try:
      > a, b = map(int, line.split(None , 2)[:2])
      > except ValueError:
      > pass
      > else:
      > if a + b == 42:
      > print b
      >
      I'm philosophically opposed to one-liners
      I'm not, as long as you don't /force/ the code into one line.
      like:
      >
      > a, b = map(int, line.split(None , 2)[:2])
      >
      because they're difficult to understand at a glance. You need to visually
      parse it and work your way out from the inside to figure out what's going
      on. Better to keep it longer and simpler.
      >
      Now that I've got my head around it, I realized there's no reason to make
      the split part so complicated. No reason to limit how many splits get
      done
      if you're explicitly going to slice the first two. And since you don't
      need to supply the second argument, the first one can be defaulted as
      well. So, you immediately get down to:
      >
      > a, b = map(int, line.split()[:2])
      I agree that the above is an improvement.
      which isn't too bad. I might take it one step further, however, and do:
      >
      > fields = line.split()[:2]
      > a, b = map(int, fields)
      >
      in fact, I might even get rid of the very generic, but conceptually
      overkill, use of map() and just write:
      >
      > a, b = line.split()[:2]
      > a = int(a)
      > b = int(b)
      If you go that route your next step is to introduce another try...except,
      one for the unpacking and another for the integer conversion...

      Peter

      Comment

      • bearophileHUGS@lycos.com

        #4
        Re: converting a sed / grep / awk / . . . bash pipe line into python

        Roy Smith:
        No reason to limit how many splits get done if you're
        explicitly going to slice the first two.
        You are probably right for this problem, because most lines are 2
        items long, but in scripts that have to process lines potentially
        composed of many parts, setting a max number of parts speeds up your
        script and reduces memory used, because you have less parts at the
        end.

        Bye,
        bearophile

        Comment

        • Roy Smith

          #5
          Re: converting a sed / grep / awk / . . . bash pipe line into python

          In article <g9lvc5$8qq$03$ 1@news.t-online.com>,
          Peter Otten <__peter__@web. dewrote:
          I might take it one step further, however, and do:
          fields = line.split()[:2]
          a, b = map(int, fields)
          in fact, I might even get rid of the very generic, but conceptually
          overkill, use of map() and just write:
          a, b = line.split()[:2]
          a = int(a)
          b = int(b)
          >
          If you go that route your next step is to introduce another try...except,
          one for the unpacking and another for the integer conversion...
          Why another try/except? The potential unpack and conversion errors exist
          in both versions, and the existing try block catches them all. Splitting
          the one line up into three with some intermediate variables doesn't change
          that.

          Comment

          • Roy Smith

            #6
            Re: converting a sed / grep / awk / . . . bash pipe line into python

            In article
            <7f2d4b4a-bc97-4b46-a31e-63f98e9fee73@34 g2000hsh.google groups.com>,
            bearophileHUGS@ lycos.com wrote:
            Roy Smith:
            No reason to limit how many splits get done if you're
            explicitly going to slice the first two.
            >
            You are probably right for this problem, because most lines are 2
            items long, but in scripts that have to process lines potentially
            composed of many parts, setting a max number of parts speeds up your
            script and reduces memory used, because you have less parts at the
            end.
            >
            Bye,
            bearophile
            Sounds like premature optimization to me. Make it work and be easy to
            understand first. Then worry about how fast it is.

            But, along those lines, I've often thought that split() needed a way to not
            just limit the number of splits, but to also throw away the extra stuff.
            Getting the first N fields of a string is something I've done often enough
            that refactoring the slicing operation right into the split() code seems
            worthwhile. And, it would be even faster :-)

            Comment

            • Peter Otten

              #7
              Re: converting a sed / grep / awk / . . . bash pipe line into python

              Roy Smith wrote:
              In article <g9lvc5$8qq$03$ 1@news.t-online.com>,
              Peter Otten <__peter__@web. dewrote:
              >
              I might take it one step further, however, and do:
              >
              > fields = line.split()[:2]
              > a, b = map(int, fields)
              >
              in fact, I might even get rid of the very generic, but conceptually
              overkill, use of map() and just write:
              >
              > a, b = line.split()[:2]
              > a = int(a)
              > b = int(b)
              >>
              >If you go that route your next step is to introduce another try...except,
              >one for the unpacking and another for the integer conversion...
              >
              Why another try/except? The potential unpack and conversion errors exist
              in both versions, and the existing try block catches them all. Splitting
              the one line up into three with some intermediate variables doesn't change
              that.
              As I understood it you didn't just split a line of code into three, but
              wanted two processing steps. These logical steps are then somewhat remixed
              by the shared error handling. You lose the information which step failed.
              In the general case you may even mask a bug.

              Peter

              Comment

              • Roy Smith

                #8
                Re: converting a sed / grep / awk / . . . bash pipe line into python

                In article <g9m6at$a71$01$ 1@news.t-online.com>,
                Peter Otten <__peter__@web. dewrote:
                Roy Smith wrote:
                >
                In article <g9lvc5$8qq$03$ 1@news.t-online.com>,
                Peter Otten <__peter__@web. dewrote:
                I might take it one step further, however, and do:

                fields = line.split()[:2]
                a, b = map(int, fields)

                in fact, I might even get rid of the very generic, but conceptually
                overkill, use of map() and just write:

                a, b = line.split()[:2]
                a = int(a)
                b = int(b)
                >
                If you go that route your next step is to introduce another try...except,
                one for the unpacking and another for the integer conversion...
                Why another try/except? The potential unpack and conversion errors exist
                in both versions, and the existing try block catches them all. Splitting
                the one line up into three with some intermediate variables doesn't change
                that.
                >
                As I understood it you didn't just split a line of code into three, but
                wanted two processing steps. These logical steps are then somewhat remixed
                by the shared error handling. You lose the information which step failed.
                In the general case you may even mask a bug.
                >
                Peter
                Well, what I really wanted was two conceptual steps, to make it easier for
                a reader of the code to follow what it's doing. My standard for code being
                adequately comprehensible is not that the reader *can* figure it out, but
                that the reader doesn't have to exert any effort to figure it out. Or even
                be aware that there's any figuring-out going on. He or she just reads it.

                Comment

                • bearophileHUGS@lycos.com

                  #9
                  Re: converting a sed / grep / awk / . . . bash pipe line into python

                  Roy Smith:
                  But, along those lines, I've often thought that split() needed a way to not
                  just limit the number of splits, but to also throw away the extra stuff.
                  Getting the first N fields of a string is something I've done often enough
                  that refactoring the slicing operation right into the split() code seems
                  worthwhile. And, it would be even faster :-)
                  Given the hypothetical .xsplit() string method I was talking about,
                  it's then easy to use islice() on it to skip the first items:

                  islice(sometext .xsplit(), 10, None)

                  Bye,
                  bearophile

                  Comment

                  Working...