Repository: xonsh/xonsh

Issue:
"command&" misparsed
`true&` is misparsed. It should execute `true` in the background, but instead treats the whole thing as the command name.

`true &` works fine.

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:
Thanks for reporting!
I don't think that this is easily fixable, since one of the ways that the parser works is that it says whitespace counts in subprocess mode. Addressing this would special case backgrounding to have whitespace not matter for the last token. That said, we probably would still want this....
This also affects `bash -c 'sleep 5 &'` invocation. Example:

> `xonsh $` indicates a xonsh prompt
> `bash $` indicates a bash prompt

``` console
xonsh $ import time; start = time.time(); bash -c 'sleep 5 &'; print(time.time() - start)
5.01800537109375

bash $ time bash -c 'sleep 5 &'
real    0m0.014s
user    0m0.008s
sys     0m0.005s
```

This make tools that use background jobs unusable in the xonsh. For example `pass -c something` (https://www.passwordstore.org/) just blocks the shell(until password is not deleted from clipboard).
Hi @corpix - I think bash is doing something very strange here.... I am not sure that is the correct behaviour to replicate.
Though the pass case is a notable usecase.
I'm having similar behavior with `nvim-qt`; instead of backgrounding like it does in other shells, it blocks the `xonsh` session until I exit `nvim-qt`.

Seems like that's probably a separate issue from this one... if I don't find an existing issue for it, I'll open one.
Hi @whitelynx - that seems like a separate issue. Would you mind opening up another issue please?
Actually, it looks like my issue is a duplicate of #2060... I'll try the hack from that issue.
On current master I can repeat this with more errors:
```xsh
echo&
```
```xsh
TRACE SUBPROC: (['echo'], '&'), captured=hiddenobject
xonsh: To log full traceback to a file set: $XONSH_TRACEBACK_LOGFILE = <filename>
Traceback (most recent call last):
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/specs.py", line 474, in _run_binary
    p = self.cls(cmd, bufsize=bufsize, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/subprocess.py", line 1955, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'echo'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/pipelines.py", line 167, in __init__
    proc = spec.run(pipeline_group=pipeline_group)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/specs.py", line 456, in run
    p = self._run_binary(kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/specs.py", line 493, in _run_binary
    raise xt.XonshError(e) from ex
xonsh.tools.XonshError: xonsh: subprocess mode: command not found: 'echo'
xonsh: To log full traceback to a file set: $XONSH_TRACEBACK_LOGFILE = <filename>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/built_ins.py", line 206, in subproc_captured_hiddenobject
    return xonsh.procs.specs.run_subproc(cmds, captured="hiddenobject", envs=envs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/specs.py", line 908, in run_subproc
    return _run_specs(specs, cmds)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/procs/specs.py", line 922, in _run_specs
    xj.add_job(
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/jobs.py", line 376, in add_job
    print_one_job(num)
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/jobs.py", line 354, in print_one_job
    info = format_job_string(num)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pc/.local/mamba-env/Users/pc/git/trace/lib/python3.12/site-packages/xonsh/jobs.py", line 347, in format_job_string
    pid = job["pids"][-1]
          ~~~~~~~~~~~^^^^
IndexError: list index out of range
```

Resolving patch:
diff --git a/xonsh/jobs.py b/xonsh/jobs.py
--- a/xonsh/jobs.py
+++ b/xonsh/jobs.py
@@ -344,9 +344,9 @@ def format_job_string(num: int) -> str:
     pos = "+" if tasks[0] == num else "-" if tasks[1] == num else " "
     status = job["status"]
     cmd = " ".join([" ".join(i) if isinstance(i, list) else i for i in job["cmds"]])
-    pid = job["pids"][-1]
+    pid = f"({job['pids'][-1]})" if job["pids"] else ""
     bg = " &" if job["bg"] else ""
-    return f"[{num}]{pos} {status}: {cmd}{bg} ({pid})"
+    return f"[{num}]{pos} {status}: {cmd}{bg} {pid}"
 
 
 def print_one_job(num, outfile=sys.stdout):


Test patch: