summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/cmd2.py50
-rw-r--r--cmd2/parsing.py13
-rw-r--r--tests/test_cmd2.py9
3 files changed, 50 insertions, 22 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 58972232..83a5a7c8 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -2214,8 +2214,7 @@ class Cmd(cmd.Cmd):
return stop
- @with_argument_list
- def do_alias(self, arglist: List[str]) -> None:
+ def do_alias(self, statement: Statement) -> None:
"""Define or display aliases
Usage: Usage: alias [name] | [<name> <value>]
@@ -2234,22 +2233,27 @@ Usage: Usage: alias [name] | [<name> <value>]
Example: alias ls !ls -lF
- If you want to use redirection or pipes in the alias, then either quote the tokens with these
- characters or quote the entire alias value.
+ If you want to use redirection or pipes in the alias, then quote them to avoid
+ the alias command itself from being redirected
Examples:
alias save_results print_results ">" out.txt
- alias save_results print_results "> out.txt"
- alias save_results "print_results > out.txt"
+ alias save_results print_results '>' out.txt
"""
+ # Get alias arguments as a list with quotes preserved
+ alias_arg_list = statement.arg_list
+
# If no args were given, then print a list of current aliases
- if not arglist:
+ if not alias_arg_list:
for cur_alias in self.aliases:
self.poutput("alias {} {}".format(cur_alias, self.aliases[cur_alias]))
+ return
+
+ # Get the alias name
+ name = alias_arg_list[0]
# The user is looking up an alias
- elif len(arglist) == 1:
- name = arglist[0]
+ if len(alias_arg_list) == 1:
if name in self.aliases:
self.poutput("alias {} {}".format(name, self.aliases[name]))
else:
@@ -2257,8 +2261,16 @@ Usage: Usage: alias [name] | [<name> <value>]
# The user is creating an alias
else:
- name = arglist[0]
- value = ' '.join(arglist[1:])
+ # Unquote redirection and pipes
+ index = 1
+ while index < len(alias_arg_list):
+ unquoted_arg = utils.strip_quotes(alias_arg_list[index])
+ if unquoted_arg in constants.REDIRECTION_TOKENS:
+ alias_arg_list[index] = unquoted_arg
+ index += 1
+
+ # Build the alias value string
+ value = ' '.join(alias_arg_list[1:])
# Validate the alias to ensure it doesn't include weird characters
# like terminators, output redirection, or whitespace
@@ -2594,24 +2606,20 @@ Usage: Usage: unalias [-a] name [name ...]
param = args.settable[0]
self.show(args, param)
- def do_shell(self, command: str) -> None:
+ def do_shell(self, statement: Statement) -> None:
"""Execute a command as if at the OS prompt.
Usage: shell <command> [arguments]"""
-
import subprocess
- try:
- # Use non-POSIX parsing to keep the quotes around the tokens
- tokens = shlex.split(command, posix=False)
- except ValueError as err:
- self.perror(err, traceback_war=False)
- return
+
+ # Get list of arguments to shell with quotes preserved
+ tokens = statement.arg_list
# Support expanding ~ in quoted paths
for index, _ in enumerate(tokens):
if tokens[index]:
- # Check if the token is quoted. Since shlex.split() passed, there isn't
- # an unclosed quote, so we only need to check the first character.
+ # Check if the token is quoted. Since parsing already passed, there isn't
+ # an unclosed quote. So we only need to check the first character.
first_char = tokens[index][0]
if first_char in constants.QUOTES:
tokens[index] = utils.strip_quotes(tokens[index])
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index b67cef10..67e97c2b 100644
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -5,7 +5,7 @@
import os
import re
import shlex
-from typing import List, Tuple, Dict
+from typing import List, Tuple, Dict, Union
from . import constants
from . import utils
@@ -105,6 +105,17 @@ class Statement(str):
rtn = None
return rtn
+ @property
+ def arg_list(self) -> Union[List[str], None]:
+ """
+ Returns a list of the arguments to the command, not including any output
+ redirection or terminators. quoted arguments remain quoted.
+ """
+ if self.args is None:
+ return None
+
+ return self.args.split()
+
def __setattr__(self, name, value):
"""Statement instances should feel immutable; raise ValueError"""
raise ValueError
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index d9ef5e78..13b53224 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -1761,6 +1761,15 @@ def test_alias(base_app, capsys):
out = run_cmd(base_app, 'alias fake')
assert out == normalize('alias fake pyscript')
+def test_alias_with_quotes(base_app, capsys):
+ # Create the alias
+ out = run_cmd(base_app, 'alias fake help ">" "out file.txt"')
+ assert out == normalize("Alias 'fake' created")
+
+ # Lookup the new alias (Only the redirector should be unquoted)
+ out = run_cmd(base_app, 'alias fake')
+ assert out == normalize('alias fake help > "out file.txt"')
+
def test_alias_lookup_invalid_alias(base_app, capsys):
# Lookup invalid alias
out = run_cmd(base_app, 'alias invalid')