diff --git a/README.md b/README.md index 213590b4..a4ce633a 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `python_execute` – appends missing `.py` when executing Python files; * `quotation_marks` – fixes uneven usage of `'` and `"` when containing args' * `rm_dir` – adds `-rf` when you trying to remove directory; +* `sed` – adds missing '/' to `sed`'s `s` commands; * `sl_ls` – changes `sl` to `ls`; * `ssh_known_hosts` – removes host from `known_hosts` on warning; * `sudo` – prepends `sudo` to previous command if it failed because of permissions; diff --git a/tests/rules/test_sed_unterminated_s.py b/tests/rules/test_sed_unterminated_s.py new file mode 100644 index 00000000..dabfeeeb --- /dev/null +++ b/tests/rules/test_sed_unterminated_s.py @@ -0,0 +1,28 @@ +import pytest +from thefuck.rules.sed_unterminated_s import match, get_new_command +from tests.utils import Command + + +@pytest.fixture +def sed_unterminated_s(): + return "sed: -e expression #1, char 9: unterminated `s' command" + + +def test_match(sed_unterminated_s): + assert match(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None) + assert match(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None) + assert match(Command('sed -e s/foo/bar -e s/baz/quz', stderr=sed_unterminated_s), None) + assert not match(Command('sed -e s/foo/bar'), None) + assert not match(Command('sed -es/foo/bar'), None) + assert not match(Command('sed -e s/foo/bar -e s/baz/quz'), None) + + +def test_get_new_command(sed_unterminated_s): + assert get_new_command(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None) \ + == 'sed -e s/foo/bar/' + assert get_new_command(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None) \ + == 'sed -es/foo/bar/' + assert get_new_command(Command(r"sed -e 's/\/foo/bar'", stderr=sed_unterminated_s), None) \ + == r"sed -e 's/\/foo/bar/'" + assert get_new_command(Command(r"sed -e s/foo/bar -es/baz/quz", stderr=sed_unterminated_s), None) \ + == r"sed -e s/foo/bar/ -es/baz/quz/" diff --git a/thefuck/rules/sed_unterminated_s.py b/thefuck/rules/sed_unterminated_s.py new file mode 100644 index 00000000..80334f94 --- /dev/null +++ b/thefuck/rules/sed_unterminated_s.py @@ -0,0 +1,17 @@ +import shlex +from thefuck.utils import quote + + +def match(command, settings): + return ('sed' in command.script + and "unterminated `s' command" in command.stderr) + + +def get_new_command(command, settings): + script = shlex.split(command.script) + + for (i, e) in enumerate(script): + if e.startswith(('s/', '-es/')) and e[-1] != '/': + script[i] += '/' + + return ' '.join(map(quote, script)) diff --git a/thefuck/utils.py b/thefuck/utils.py index bf1980e8..5c6b0264 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -7,6 +7,13 @@ from .types import Command DEVNULL = open(os.devnull, 'w') +if six.PY2: + import pipes + quote = pipes.quote +else: + import shlex + quote = shlex.quote + def which(program): """Returns `program` path or `None`."""