Execute commands on fish of WSL via subprocess

Viewed 509

I have installed WSL(Ubuntu) and now I'm wanting to access fish from WSL and execute a command like date from it. How can I do so?

Here's is my attempt!

import subprocess
subprocess.run(["wsl", "fish"], shell=True)
subprocess.run(["date"], shell=True)

The output shows the execution until the 2nd line of the code but not the execution of date command in the fish terminal. Any solution to do so?

3 Answers

With your command, the first subprocess.run() starts a process and then waits for it to finish. As you're aware, wsl fish doesn't actually finish by itself, meaning your python app will now block indefinitely.

What you actually want to do is start a process, send data to it through its STDIN, and read its STDOUT output.

You can't quite do it like this though- for 1, you don't want to block the interpreter!

proc = Popen(["wsl", "fish"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = proc.communicate(b"date")
print(output)

communicate also blocks. If you wanted to keep this running you'd do proc.stdin.write() followed by proc.stdout.readline()

Assuming that you just want to capture the output, the most efficient invocation should be:

cp = subprocess.run(["wsl", "-e", "date"], capture_output=True)
date = cp.stdout

By specifying -e, you skip the shell entirely, which avoids the overhead of the shell starting up.

If you really do want to force it to run within fish:

cp = subprocess.run(["wsl", "-e", "fish", "-c", "date"], capture_output=True)
date = cp.stdout

More detail:

If fish is your default shell, then you really don't need to specify it as an argument to the wsl command. You can see this by trying the following in PowerShell or CMD:

wsl ps -eH
  PID TTY          TIME CMD
    1 ?        00:00:00 init
   21 ?        00:00:00   init
   22 ?        00:00:00     init
   23 pts/0    00:00:00       fish
   30 pts/0    00:00:00         ps

Note that the ps command, although run without fish on the WSL command-line, still runs under the default shell.

However, you don't have to. If a command doesn't need the shell, you can run it directly without going through a shell (avoids overhead) via:

wsl -e ps -ef
  PID TTY          TIME CMD
    1 ?        00:00:00 init
   11 ?        00:00:00   init
   12 ?        00:00:00     init
   13 pts/0    00:00:00       ps

You'll notice that no shell is running. This is the variation I recommended above with the ["wsl","-e","date"] in Python.

Let's say that fish isn't your default shell, and yet you do want it to run.

wsl -e fish -c "ps -eH"
  PID TTY          TIME CMD
    1 ?        00:00:00 init
   20 ?        00:00:00   init
   21 ?        00:00:00     init
   22 pts/0    00:00:00       fish
   29 pts/0    00:00:00         ps

But don't do wsl fish "ps -eH" (or wsl fish -c date). That will run fish within fish, which is very wasteful:

   PID TTY          TIME CMD
    1 ?        00:00:00 init
   11 ?        00:00:00   init
   12 ?        00:00:00     init
   13 pts/0    00:00:00       fish
   20 pts/0    00:00:00         fish
   22 pts/0    00:00:00           ps

The first subprocess.run will start an interactive fish shell.

Only when you type exit, then date will run.

Related