Search code examples
pythonxonsh

How to pipe In the xonsh shell the output of a Python construct to another command?


In the xonsh shell how can I pipe the output of a Python construct to another command? Desired example:

for v in ${...}: print ("{}={}".format(v,${v})) | head

In this for v in ... is the Python construct and head is the command I want to pipe its output through.

The command line above does not work; I get always the following error:

NameError: name 'head' is not defined

Looks like xonsh doesn't leave the Python-mode for the pipe symbol (see "Pipes" in the xonsh docs). - So, how can I instruct xonsh to understand the pipe character here as a subprocess-mode symbol?


Solution

  • Xonsh cannot support piping arbitrary Python code to subprocesses, because subprocesses only accept string inputs. Therefore xonsh only accepts Python expressions that are strings, contain strings (e.g. lists of strs), or return strings (e.g. functions). The reason that xonsh only accepts expressions for subprocesses is that the subprocess itself is implemented as an expression.

    The problem with the example code above is that you are attempting to pipe a statement (a for-statement, specifically) into a subprocess (which is an expression). This is not syntactically valid in pure Python and isn't in xonsh because it is not clear which of the statements in | the expression after the | should be applied to.

    To fix the above, you need to change the for-loop to be an expression. Here are a couple of examples how:

    # use a list comprehension
    echo @(["{}={}\n".format(v,${v}) for v in ${...}]) | head
    
    # use a function
    def f(): 
        for v in ${...}: 
            print("{}={}".format(v,${v}))
    
    @(f) | head