Skip to content

Instantly share code, notes, and snippets.

@rduplain
Last active October 21, 2018 19:32
Show Gist options
  • Save rduplain/3441687 to your computer and use it in GitHub Desktop.
Save rduplain/3441687 to your computer and use it in GitHub Desktop.
Wrap sed in Python to provide ed-style line editing in a simple function call.
"Wrapper to provide ed-style line editing."
# Ron DuPlain <ron.duplain@gmail.com>
from subprocess import Popen, PIPE
def edit(text, command):
r"""Edit given text using ed-style line editing, using system's sed command.
Examples:
>>> edit('Hello, world!', 's/world/friends/')
u'Hello, friends!'
>>> edit('Hello, world!', 'bad_input')
Traceback (most recent call last):
...
EditorException: sed: can't find label for jump to `ad_input'
>>>
Boring empty cases are handled gracefully:
>>> edit('Hello, world!', '')
u'Hello, world!'
>>> edit('', 's/world/friends/')
u''
>>> edit('', '')
u''
>>>
This is a full ed implementation:
>>> edit('Hello hello, world! HELLO!', 's/hello/greetings/gi')
u'greetings greetings, world! greetings!'
>>> edit('Hello, world!', r's/\(.*\)/You said, "\1"/')
u'You said, "Hello, world!"'
>>>
A note on backslash escaping for sed, remember that Python has it's own
escaping. For example, "backslash one" must either be \\1 or provided in a
raw string, r'\1'.
>>> edit('Hello, world!', 's/\\(.*\\)/\\U\\1/')
u'HELLO, WORLD!'
>>> edit('Hello, world!', r's/\(.*\)/\U\1/')
u'HELLO, WORLD!'
>>>
"""
output = sed_wrapper(text, command)
if not output:
return u''
return unicode(output)
class EditorException(RuntimeError):
"An error occured while processing the editor command."
def sed_wrapper(text, command):
"""Wrap sed to provide full ed-style line-editing.
Being the stream editor, open sed in a subprocess and communicate with it
using stdio, raising a sensible exception if the command call does not
succeed.
Note this wrapper deals with shell injection attempts:
>>> sed_wrapper('Hello, world!', 's/world/friends/; ls')
Traceback (most recent call last):
...
EditorException: sed: -e expression #1, char 20: extra characters after command
>>> sed_wrapper('Hello, world!', '; ls')
Traceback (most recent call last):
...
EditorException: sed: -e expression #1, char 4: extra characters after command
>>>
"""
arguments = ['sed', command]
sed = Popen(arguments, stdin=PIPE, stdout=PIPE, stderr=PIPE)
sed.stdin.write(text)
sed.stdin.close()
returncode = sed.wait()
if returncode != 0:
# Unix integer returncode, where 0 is success.
raise EditorException(sed.stderr.read().strip())
return sed.stdout.read()
if __name__ == '__main__':
# Test with `pyflakes editor.py && python editor.py`.
# If you do not have pyflakes, get it! `pip install pyflakes`
# See output for errors. No output means that all tests pass.
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment