mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Allow stdout to redirect to stderr
This commit is contained in:
parent
c411ef7245
commit
74480d8b2d
6 changed files with 81 additions and 10 deletions
|
@ -725,7 +725,7 @@ with the following syntax:
|
|||
>>> COMMAND err>o
|
||||
>>> COMMAND e>out
|
||||
>>> COMMAND e>o
|
||||
>>> COMMAND 2>&1 # included for Bash compatibility
|
||||
>>> COMMAND 2>&1 # included for Bash compatibility
|
||||
|
||||
This merge can be combined with other redirections, including pipes (see the
|
||||
section on `Pipes`_ above):
|
||||
|
@ -737,6 +737,16 @@ section on `Pipes`_ above):
|
|||
|
||||
It is worth noting that this last example is equivalent to: ``COMMAND a> combined.txt``
|
||||
|
||||
Similarly, you can also send stdout to stderr with the following syntax:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
>>> COMMAND out>err
|
||||
>>> COMMAND out>e
|
||||
>>> COMMAND o>err
|
||||
>>> COMMAND o>e
|
||||
>>> COMMAND 1>&2 # included for Bash compatibility
|
||||
|
||||
Redirecting ``stdin``
|
||||
---------------------
|
||||
|
||||
|
@ -1037,8 +1047,8 @@ regex globbing:
|
|||
def regexsearch(s):
|
||||
s = expand_path(s)
|
||||
return reglob(s)
|
||||
|
||||
|
||||
|
||||
|
||||
<function xonsh.built_ins.regexsearch>
|
||||
|
||||
Note that both help and superhelp return the object that they are inspecting.
|
||||
|
|
13
news/to2.rst
Normal file
13
news/to2.rst
Normal file
|
@ -0,0 +1,13 @@
|
|||
**Added:**
|
||||
|
||||
* Users may now redirect stdout to stderr in subprocess mode.
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:** None
|
||||
|
||||
**Security:** None
|
|
@ -157,6 +157,25 @@ def test_script(case):
|
|||
assert exp_rtn == rtn
|
||||
|
||||
|
||||
ALL_PLATFORMS_STDERR = [
|
||||
# test redirecting a function alias
|
||||
("""
|
||||
def _f(args, stdout):
|
||||
print('Wow Mom!', file=stdout)
|
||||
|
||||
aliases['f'] = _f
|
||||
f o>e
|
||||
""", "Wow Mom!\n", 0),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('case', ALL_PLATFORMS_STDERR)
|
||||
def test_script_stder(case):
|
||||
script, exp_err, exp_rtn = case
|
||||
out, err, rtn = run_xonsh(script, stderr=sp.PIPE)
|
||||
assert exp_err == err
|
||||
assert exp_rtn == rtn
|
||||
|
||||
|
||||
@skip_if_on_windows
|
||||
@pytest.mark.parametrize('cmd, fmt, exp', [
|
||||
('pwd', None, lambda: os.getcwd() + '\n'),
|
||||
|
|
|
@ -1816,8 +1816,8 @@ def test_redirect_all(case):
|
|||
assert check_xonsh_ast({}, '$[echo "test" {}> test.txt < input.txt]'.format(case), False)
|
||||
|
||||
@pytest.mark.parametrize('r',
|
||||
['e>o', 'e>out', 'err>o', 'err>o',
|
||||
' 2>1', 'e>1', 'err>1', '2>out',
|
||||
['e>o', 'e>out', 'err>o',
|
||||
'2>1', 'e>1', 'err>1', '2>out',
|
||||
'2>o', 'err>&1', 'e>&1', '2>&1'
|
||||
])
|
||||
@pytest.mark.parametrize('o', ['', 'o', 'out', '1'])
|
||||
|
@ -1826,6 +1826,17 @@ def test_redirect_error_to_output(r, o):
|
|||
assert check_xonsh_ast({}, '$[< input.txt echo "test" {} {}> test.txt]'.format(r, o), False)
|
||||
assert check_xonsh_ast({}, '$[echo "test" {} {}> test.txt < input.txt]'.format(r, o), False)
|
||||
|
||||
@pytest.mark.parametrize('r',
|
||||
['o>e', 'o>err', 'out>e',
|
||||
'1>2', 'o>2', 'out>2', '1>err',
|
||||
'1>e', 'out>&2', 'o>&2', '1>&2'
|
||||
])
|
||||
@pytest.mark.parametrize('e', ['e', 'err', '2'])
|
||||
def test_redirect_output_to_error(r, e):
|
||||
assert check_xonsh_ast({}, '$[echo "test" {} {}> test.txt]'.format(r, e), False)
|
||||
assert check_xonsh_ast({}, '$[< input.txt echo "test" {} {}> test.txt]'.format(r, e), False)
|
||||
assert check_xonsh_ast({}, '$[echo "test" {} {}> test.txt < input.txt]'.format(r, e), False)
|
||||
|
||||
def test_macro_call_empty():
|
||||
assert check_xonsh_ast({}, 'f!()', False)
|
||||
|
||||
|
|
|
@ -265,6 +265,10 @@ _E2O_MAP = LazyObject(lambda: frozenset({'{}>{}'.format(e, o)
|
|||
for e in _REDIR_ERR
|
||||
for o in _REDIR_OUT
|
||||
if o != ''}), globals(), '_E2O_MAP')
|
||||
_O2E_MAP = LazyObject(lambda: frozenset({'{}>{}'.format(o, e)
|
||||
for e in _REDIR_ERR
|
||||
for o in _REDIR_OUT
|
||||
if o != ''}), globals(), '_O2E_MAP')
|
||||
|
||||
|
||||
def _is_redirect(x):
|
||||
|
@ -323,10 +327,14 @@ def _parse_redirects(r):
|
|||
def _redirect_streams(r, loc=None):
|
||||
"""Returns stdin, stdout, stderr tuple of redirections."""
|
||||
stdin = stdout = stderr = None
|
||||
no_ampersand = r.replace('&', '')
|
||||
# special case of redirecting stderr to stdout
|
||||
if r.replace('&', '') in _E2O_MAP:
|
||||
if no_ampersand in _E2O_MAP:
|
||||
stderr = subprocess.STDOUT
|
||||
return stdin, stdout, stderr
|
||||
elif no_ampersand in _O2E_MAP:
|
||||
stdout = 2 # using 2 as a flag, rather than using a file object
|
||||
return stdin, stdout, stderr
|
||||
# get streams
|
||||
orig, mode, dest = _parse_redirects(r)
|
||||
if mode == 'r':
|
||||
|
@ -718,6 +726,10 @@ def _update_last_spec(last):
|
|||
r, w = pty.openpty() if use_tty else os.pipe()
|
||||
last.stderr = safe_open(w, 'w')
|
||||
last.captured_stderr = safe_open(r, 'r')
|
||||
# redirect stdout to stderr, if we should
|
||||
if isinstance(last.stdout, int) and last.stdout == 2:
|
||||
# need to use private interface to avoid duplication.
|
||||
last._stdout = last.stderr
|
||||
|
||||
|
||||
def cmds_to_specs(cmds, captured=False):
|
||||
|
|
|
@ -227,10 +227,16 @@ SearchPath = r"((?:[rgp]+|@\w*)?)`([^\n`\\]*(?:\\.[^\n`\\]*)*)`"
|
|||
# longest operators first (e.g., if = came before ==, == would get
|
||||
# recognized as two instances of =).
|
||||
_redir_names = ('out', 'all', 'err', 'e', '2', 'a', '&', '1', 'o')
|
||||
_e2o_map = ('err>out', 'err>&1', '2>out', 'err>o', 'err>1', 'e>out', 'e>&1',
|
||||
'2>&1', 'e>o', '2>o', 'e>1', '2>1')
|
||||
IORedirect = group(group(*_e2o_map), '{}>>?'.format(group(*_redir_names)))
|
||||
_redir_check = set(_e2o_map)
|
||||
_redir_map = (
|
||||
# stderr to stdout
|
||||
'err>out', 'err>&1', '2>out', 'err>o', 'err>1', 'e>out', 'e>&1',
|
||||
'2>&1', 'e>o', '2>o', 'e>1', '2>1',
|
||||
# stdout to stderr
|
||||
'out>err', 'out>&2', '1>err', 'out>e', 'out>2', 'o>err', 'o>&2',
|
||||
'1>&2', 'o>e', '1>e', 'o>2', '1>2',
|
||||
)
|
||||
IORedirect = group(group(*_redir_map), '{}>>?'.format(group(*_redir_names)))
|
||||
_redir_check = set(_redir_map)
|
||||
_redir_check = {'{}>'.format(i) for i in _redir_names}.union(_redir_check)
|
||||
_redir_check = {'{}>>'.format(i) for i in _redir_names}.union(_redir_check)
|
||||
_redir_check = frozenset(_redir_check)
|
||||
|
|
Loading…
Add table
Reference in a new issue