17

I know of python -c '<code>', but I'm wondering if there's a more elegant python equivalent to perl -pi -e '<code>'. I still use it quite a bit for things like find and replace in a whole directory (perl -pi -e s/foo/bar/g * or even find . | xargs perl -pi -e s/foo/bar/g for sub-directories).

I actually feel that that which makes Perl Perl (free form Tim Toady-ness) is what makes perl -pi -e work so well, while with Python you'd have to do something along the lines of importing the re module, creating an re instance and then capture stdin, but maybe there's a Python shortcut that does all that and I missed it (sorely missed it)...

Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
Silfheed
  • 11,585
  • 11
  • 55
  • 67

6 Answers6

10

The command line usage from 'python -h' certainly strongly suggests there is no such equivalent. Perl tends to make extensive use of '$_' (your examples make implicit use of it), and I don't think Python supports any similar concept, thereby making Python equivalents of the Perl one-liners much harder.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
10

An equivalent to -pi isn't that hard to write in Python.

  1. Write yourself a handy module with the -p and -i features you really like. Let's call it pypi.py.

  2. Use python -c 'import pypi; pypi.subs("this","that")'

You can implement the basic -p loop with the fileinput module.

You'd have a function, subs that implements the essential "-i" algorithm of opening a file, saving the backup copy, and doing the substitute on each line.

There are some activestate recipes like this. Here are some:

Not built-in. But not difficult to write. And once written easy to customize.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 3
    I would suggest that the module is called pi.py, is stored inside the site-packages directory, ends with a `if __name__ == "__main__":` construct, and then called with `python -mpi ...` – tzot Dec 15 '08 at 09:22
9

I know this is a couple of years too late, but I've recently found a very nice tool called pyp, which does exactly what you've asked for.

I think your command should be:

pyp "p.replace('foo','bar')"
jarondl
  • 1,593
  • 4
  • 18
  • 27
  • 1
    Nice find! pyp ("The Pyed Piper, Piping Python Through Pipes"), from Sony Pictures Imageworks, is very concise, flexible, can do statistics, internal pipelines, etc. It was presented at PYCON 2012 "The Pyed Piper: A Modern Python Alternative to awk, sed and Other Unix Text Manipulation Utilities by Toby Rosen" – nealmcb Mar 29 '13 at 20:30
  • 1
    Is the `pypi.py` code maintained? Python 3? I looked at the video and the archives I can find date back to 2012-2015 — I found nothing more recent. Am I missing the obvious (such as "it doesn't need to be maintained — it just works")? – Jonathan Leffler Nov 18 '19 at 21:31
6

I think perl is better suited for this kind of on the fly scripting. If you want quick on the fly one-off scripting capability I recommend sticking to perl, awk, sed, and standard unix command-line tools.

But if your interested in using python, I use optparse to write my own command line tools and recommend it. optparse provides a clean and easy-to-use command line option parser with built in help generation.

Here's a sample:

def myfunc(filename, use_versbose):
   # function code

if __name__ == '__main__':
    from optparse import OptionParser

    parser = OptionParser()
    parser.add_option("-f", "--file", dest="filename",
                      help="write report to FILE", metavar="FILE")
    parser.add_option("-q", "--quiet",
                      action="store_false", dest="verbose", default=True,
                      help="don't print status messages to stdout")

    (options, args) = parser.parse_args()

    if options.filename:
        myfunc(options.filename, options.verbose)

    else:
        print 'ERROR -- Necessary command line options not given!'
        print parser.print_help()

parser.print_help() generates the following output, and is automatically displayed when -h or --help is given at the command line:

usage: <yourscript> [options]

options:
  -h, --help            show this help message and exit
  -f FILE, --file=FILE  write report to FILE
  -q, --quiet           don't print status messages to stdout
monkut
  • 42,176
  • 24
  • 124
  • 155
  • 3
    This really is in no way a replacement for a one-liner, and hence most of this is not an answer to the question – Leon Timmermans Dec 15 '08 at 03:01
  • 3
    Yes, but perhaps the more elegant solution isn't the equivelent to `perl -pi -e`, but writing your own command line tools for often performed tasks. "myscript.py -x this -y that" may be cleaner than "python -c 'import this; dostuff(this,that)'". It depends on your individual needs and preferences. – monkut Dec 15 '08 at 06:43
2

The fileinput module has the capability for in-place editing. You can't dispense with the backups, though. Here's how to use it for a one-liner:

python -c 'import fileinput, sys; for line in fileinput.input(inplace=True): sys.stdout.write(line, "foo", "bar")'

Personally, I just usually use Perl when I need to do something like this. It is one of the few times I use Perl.

Michael Hoffman
  • 32,526
  • 7
  • 64
  • 86
0

The above may work for stdin, but does not look like it would work for a file.

Maybe something like:

--

import fileinput 
import sys
for line in fileinput.input("./poop", inplace=True):
    line = line.replace("foo", "bar")
    sys.stdout.write(line)

-- where "./poop" can be replaced with where your file is and line.replace should support things like line.replace("{}".format(variablehere),"newtext")

cgseller
  • 3,875
  • 2
  • 19
  • 21