summaryrefslogtreecommitdiff
path: root/cmd2/parsing.py
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2019-05-13 23:59:03 -0400
committerGitHub <noreply@github.com>2019-05-13 23:59:03 -0400
commit3ee97d121887d3055fc6326b1d9bc290f5235866 (patch)
treef5695aece2c4e6173513da3f436df73099b88c09 /cmd2/parsing.py
parentcbf0313306c99c02f3c503f60d70df4bda2cce64 (diff)
parent6c051808d83b75108c0549acbc97fe2201f8de63 (diff)
downloadcmd2-git-3ee97d121887d3055fc6326b1d9bc290f5235866.tar.gz
Merge pull request #676 from python-cmd2/pipe_chaining
Pipe chaining
Diffstat (limited to 'cmd2/parsing.py')
-rw-r--r--cmd2/parsing.py87
1 files changed, 43 insertions, 44 deletions
diff --git a/cmd2/parsing.py b/cmd2/parsing.py
index 934f1d26..8febd270 100644
--- a/cmd2/parsing.py
+++ b/cmd2/parsing.py
@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
"""Statement parsing classes for cmd2"""
-import os
import re
import shlex
from typing import Dict, Iterable, List, Optional, Tuple, Union
@@ -160,13 +159,13 @@ class Statement(str):
# characters appearing after the terminator but before output redirection, if any
suffix = attr.ib(default='', validator=attr.validators.instance_of(str))
- # if output was piped to a shell command, the shell command as a list of tokens
- pipe_to = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list))
+ # if output was piped to a shell command, the shell command as a string
+ pipe_to = attr.ib(default='', validator=attr.validators.instance_of(str))
# if output was redirected, the redirection token, i.e. '>>'
output = attr.ib(default='', validator=attr.validators.instance_of(str))
- # if output was redirected, the destination file
+ # if output was redirected, the destination file token (quotes preserved)
output_to = attr.ib(default='', validator=attr.validators.instance_of(str))
def __new__(cls, value: object, *pos_args, **kw_args):
@@ -208,7 +207,7 @@ class Statement(str):
rtn += ' ' + self.suffix
if self.pipe_to:
- rtn += ' | ' + ' '.join(self.pipe_to)
+ rtn += ' | ' + self.pipe_to
if self.output:
rtn += ' ' + self.output
@@ -453,56 +452,56 @@ class StatementParser:
arg_list = tokens[1:]
tokens = []
- # check for a pipe to a shell process
- # if there is a pipe, everything after the pipe needs to be passed
- # to the shell, even redirected output
- # this allows '(Cmd) say hello | wc > countit.txt'
- try:
- # find the first pipe if it exists
- pipe_pos = tokens.index(constants.REDIRECTION_PIPE)
- # save everything after the first pipe as tokens
- pipe_to = tokens[pipe_pos + 1:]
+ pipe_to = ''
+ output = ''
+ output_to = ''
- for pos, cur_token in enumerate(pipe_to):
- unquoted_token = utils.strip_quotes(cur_token)
- pipe_to[pos] = os.path.expanduser(unquoted_token)
+ # Find which redirector character appears first in the command
+ try:
+ pipe_index = tokens.index(constants.REDIRECTION_PIPE)
+ except ValueError:
+ pipe_index = len(tokens)
- # remove all the tokens after the pipe
- tokens = tokens[:pipe_pos]
+ try:
+ redir_index = tokens.index(constants.REDIRECTION_OUTPUT)
except ValueError:
- # no pipe in the tokens
- pipe_to = []
+ redir_index = len(tokens)
- # check for output redirect
- output = ''
- output_to = ''
try:
- output_pos = tokens.index(constants.REDIRECTION_OUTPUT)
- output = constants.REDIRECTION_OUTPUT
+ append_index = tokens.index(constants.REDIRECTION_APPEND)
+ except ValueError:
+ append_index = len(tokens)
- # Check if we are redirecting to a file
- if len(tokens) > output_pos + 1:
- unquoted_path = utils.strip_quotes(tokens[output_pos + 1])
- output_to = os.path.expanduser(unquoted_path)
+ # Check if output should be piped to a shell command
+ if pipe_index < redir_index and pipe_index < append_index:
- # remove all the tokens after the output redirect
- tokens = tokens[:output_pos]
- except ValueError:
- pass
+ # Get the tokens for the pipe command and expand ~ where needed
+ pipe_to_tokens = tokens[pipe_index + 1:]
+ utils.expand_user_in_tokens(pipe_to_tokens)
- try:
- output_pos = tokens.index(constants.REDIRECTION_APPEND)
- output = constants.REDIRECTION_APPEND
+ # Build the pipe command line string
+ pipe_to = ' '.join(pipe_to_tokens)
+
+ # remove all the tokens after the pipe
+ tokens = tokens[:pipe_index]
+
+ # Check for output redirect/append
+ elif redir_index != append_index:
+ if redir_index < append_index:
+ output = constants.REDIRECTION_OUTPUT
+ output_index = redir_index
+ else:
+ output = constants.REDIRECTION_APPEND
+ output_index = append_index
# Check if we are redirecting to a file
- if len(tokens) > output_pos + 1:
- unquoted_path = utils.strip_quotes(tokens[output_pos + 1])
- output_to = os.path.expanduser(unquoted_path)
+ if len(tokens) > output_index + 1:
+ unquoted_path = utils.strip_quotes(tokens[output_index + 1])
+ if unquoted_path:
+ output_to = utils.expand_user(tokens[output_index + 1])
- # remove all tokens after the output redirect
- tokens = tokens[:output_pos]
- except ValueError:
- pass
+ # remove all the tokens after the output redirect
+ tokens = tokens[:output_index]
if terminator:
# whatever is left is the suffix