Subprocess Strings¶
Strings in xonsh follow two simple rules:
Strings in xonsh are always parsed in the same way, and
Python always wins!
Together these rules mean that even strings in subprocess mode are treated like Python strings! This will (help) preserve your sanity.
No Escape¶
Xonsh strings are exactly like Python strings everywhere. Xonsh uses exactly the same escape characters that Python does; no more and no less.
bash
$ echo A\ Single\ Argument
A Single Argument
In the above example, since the spaces are escaped, the echo command
only receves a single argument. Xonsh does not allow this. If you were
to try this in xonsh, you’d see:
xonsh
@ echo Actually\ Three\ Arguments
Actually\ Three\ Arguments
In this example, echo recives three arguments:: "Actually\\", "Three\\",
and "Arguments". Instead, xonsh requires you to use quotes in order to
pass in a single argument:
xonsh or bash
$ echo "A Single Argument"
A Single Argument
Using quotes is arguably what should have been done in sh-lang in the first place.
Note
When in doubt in subprocess mode, use quotes!
Justification¶
The reasons for not having additional escape sequences, as in sh-langs, are:
Escape charaters can get overwhemlingly ugly, fast.
We have escape characters, they are called quotes :)
We have literal input in subprocess mode via macros.
On this last point, if you don’t already know about
Subprocess Macros,
these allow all input following an ! to be treated as a single argument.
For example,
xonsh
@ echo! A Single Argument
A Single Argument
Subprocess macros are the ultimate escape mechanism.
The Quotes Stay¶
In sh-langs, internal quote characters are removed. For instance:
$ echo foo"bar"baz
foobarbaz
$ echo --key="value"
--key=value
Xonsh considers this behavior suboptimal. Instead, xonsh treats these
arguments as if they were surrounded in another, outer level of
quotation ('foo"bar"baz'). Xonsh will keep the quotation marks
when leading and trailing quotes are not matched.
xonsh
@ echo foo"bar"baz
foo"bar"baz
@ echo --key="value"
--key="value"
You can think of these being equivalent to,
xonsh
@ echo 'foo"bar"baz'
foo"bar"baz
@ echo '--key="value"'
--key="value"
This is yet another major point of departure for xonsh from traditional shells. However, the xonsh subprocess string handling is consistent and predictable.
Environment Variable Substitution¶
In subprocess mode, $NAME inside strings is replaced with the value
of the environment variable. This happens automatically for regular and
f-strings, but not for raw strings:
echo $HOME
# /home/snail
echo "$HOME"
# /home/snail
echo r"$HOME"
# $HOME -- raw strings suppress substitution
echo $HOME/docs # subprocess mode -- substituted inline
# /home/snail/docs
del $MY_VAR
See String Literal Prefixes below for a full comparison of which string types perform substitution.
String Literal Prefixes¶
For fine control of environment variable substitutions, brace substitutions, and backslash escapes, xonsh supports an extended set of string literal prefixes:
""— regular string: backslash escapes. Envvar substitutions in subprocess mode.r""— raw string: unmodified.f""— formatted string: brace substitutions, backslash escapes. Envvar substitutions in subprocess mode.fr""— raw formatted string: brace substitutions.p""— path string: backslash escapes, envvar substitutions, returns Path.pr""— raw path string: envvar substitutions, returns Path.pf""— formatted path string: backslash escapes, brace and envvar substitutions, returns Path.
To understand the differences, set $EVAR to 1 and var to 2:
String literal |
As python object |
print(<String literal>) |
echo <String literal> |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String Tricks¶
Triple Quotes¶
To avoid escape characters (e.g. echo "\"hello\""), use triple
quotes:
echo """{"hello":'world'}"""
# {"hello":'world'}
Creating Files with Multiline Strings¶
Combine triple quotes with @() and a redirect to create files
without heredoc syntax:
echo @("""
line 1
line 2
line 3
""".strip()) > file.txt
Multiline Arguments¶
Use @() with triple quotes to pass a multiline string as a single
argument:
python -c @("""
import sys
for i in range(3):
print(i, sys.platform)
""")
f-strings in Commands¶
You can use @() to inject an f-string, but f-strings also work
directly in subprocess mode:
echo @(f'Hello {$HOME}')
# Hello /home/snail
# f-strings work by themselves too:
echo f'Hello {$HOME}'
# Hello /home/snail
Splitting Strings Like the Shell¶
Xonsh’s Lexer.split() can
split a string into tokens the same way the shell does – respecting
quoting, escapes, and operators. This is the function used internally
by the @$() operator:
from xonsh.parsers.lexer import Lexer
Lexer().split('echo "hello world" file.txt')
# ['echo', '"hello world"', 'file.txt']
See also¶
Subprocess – subprocess operators and capturing modes
Environment – environment variables and
@.env.swap()Globbing – glob and regex path expansion
Macros – subprocess macros for literal input
Quote removal in subprocess mode does not behave as expected