16

I usually use the linux subsystem when I'm programming anything on Windows 10 so all my paths are relative to ~. I have a python script that runs forever in the background until I kill the process. How would I do that on Windows 10 bash without an open terminal?

Things I've tried:

  • bash -c "python3 script.py from Run.
  • nohup python3 -u script.py then closing the terminal.
  • setsid python3 script.py then closing the terminal.

None of these worked. Is there a way to do this? Alternatively, is there a way to alter the paths so they work if I run the script from W10 AND bash without having to switch them around every time?

7 Answers 7

10

A recent addition to WSL allows for starting wsl commands directly from the 'run' or Start menu. You can append an ampersand to the command (normal shell behavior), which results in a momentary bash terminal which immediately disappears but the command continues.

Examples, within Start » Run:

wsl sleep 20 &
wsl python -c 'import time; time.sleep(20);' &

If you go into Windows' Task Manager, it will show a Sleep or Python2 command running for 20 seconds, then self-clearing.

One thing I've found is that environment variables are not available. For instance, DISPLAY, if set in the windows normal method, is not passed to WSL. For this, there needs to be a way to pass these variables. Even if the command does not support setting the needed variable via a command line argument, it's possible to do it using bash itself:

# direct, command-dependent
wsl emacs --display=:0 &

# indirect, more flexible
wsl bash -c "DISPLAY=:0 emacs" &

NB: I'm currently running win10_64, Version 1709 (OS Build 16299.64).

1
  • This appears to no longer work in Win11 and WSL2 (and WSLg).
    – r2evans
    Commented Nov 12, 2021 at 15:28
15

Update

Microsoft has addressed this. Background / daemon processes are now allowed to continue running even after bash.exe (or other WSL launcher process) is closed. A recent build of Win10 (spring 2018 for public releases, build 17046 or greater) is required.

The below is preserved for posterity.


Sadly/absurdly, there is no way to do this. Microsoft, in their infinite wisdom, have decided that WSL (Windows Subsystem for Linux) will only run while you have a bash.exe process open. Close the last one (or possibly even close the last window; I'm not sure if it'll tolerate being run headless) and WSL shuts down, killing all of its processes.

The justification for this was "to conserve resources", which is absurd on several different levels but most notably because, dammit, my computer has those resources and they are there to be used! If I want a process to run, it should run; if I don't want it to run, I can kill it. For something explicitly intended as a developer tool, it sometimes feels like WSL is only usable as a plaything and its users can't be trusted to know what they're doing.

Anyhow, if you want this fixed, vote for Consider enabling cron jobs, daemons and background tasks on the UserVoice page. It's currently the second-most-voted-for request, and is "on the backlog".

6
  • "most notably because, dammit, my computer has those resources and they are there to be used" very well put! it really boggles the mind...
    – airstrike
    Commented Jul 6, 2017 at 0:27
  • I have build 17134 and can't have a background job without a bash window.
    – John Pick
    Commented Aug 20, 2018 at 22:30
  • @JohnPick They won't launch automatically after a reboot, but they should stay running when the window is closed (as long as they aren't attached to it, of course).
    – CBHacking
    Commented Aug 22, 2018 at 6:18
  • @CBHacking To be more specific, if I start a Node.js server in the background, then it's both a job (jobs) and a process (ps aux). I can use fg to bring it to the foreground. But, after I close the last bash window and open a new bash window, the job is gone, the process is still running, and I can't move the process to the foreground. Sorry if my terminology is off.
    – John Pick
    Commented Aug 23, 2018 at 16:45
  • @JohnPick Ah, that's a completely different issue - this question is about processes, not about shell job management - and ought to have been asked elsewhere, but the answer is simple enough I'll give it here: launch the process under tmux or screen to support re-attaching to a different terminal. Linux (and other *nix) machines running natively have the same limitation.
    – CBHacking
    Commented Aug 24, 2018 at 2:32
2

Yeah, it's "impossible" at the moment.

But it's possible to have it "appear" like a background process with some trickery. I wanted this functionality pretty bad myself so after a couple of hours I came up with a shitty but working solution.

The main point is to create an invisible shell that you launch WSL Bash to with VBScript. You can then have that script be run on startup. Proper Task Scheduling didn't work for some strange reason.

Linux side to enable daemons you can have your own rudimentary startup system that abuses .bashrc for example.

The process is detailed here in this document I wrote https://emil.fi/bashwin. I didn't implement task monitoring but it should be pretty easy to extend.

0
2

Have you tried this solution?

It uses a WSH helper to launch any application hidden.

Then you can just create a new task in Tak Scheduler to launch the command when you login. Something like wscript <path to runHidden.vbs> bash.exe -c "python script.py"

2

It took me forever but I found a stupidly complicated way of doing it (from a batch file):

start bash -c "DISPLAY=:0 [command] & (sleep 0.5 && kill -n 9 $$)"

Here's a breakdown of what it does and why:

  • `start`: to make the batch file window go away
  • `bash -c`: lets you run a bash command
  • `DISPLAY=:0`: sets your X-server
  • `[command]`: your command/commands (`[command && [command]`)
  • `&`: to make the next command run after it starts and not when it's done
  • `sleep 0.5`: to make sure the process has started
  • `&&`: to make the next command run after it's done and not when it starts
  • `kill -n 9 $$`: to kill the bash shell so it's only the graphical application

Note: DISPLAY=:0 sets it to the x-server at :0. To change it to (eg) :1, do DISPLAY=:1 etc.

Note: start is only required if it's from a batch script. If it's from the terminal, you don't need this

Note: sleep needs to be set differently for each application. You might even need to omit it.

0

I don't know how well Windows Service Manager (SrvMan) from http://tools.sysprogs.org/srvman/ would work for you, but it has worked for me for other programs. In fact, I did tried running the "bash.exe" as a service to see if it would work, and I'm guessing I have to tinker around a bit more to get LAMP to actually work in the background.

1
0

I ran into the same problem: Windows 10 wsl2 Ubuntu Linux isn't working in background, if I have wsl console session closed. I found my way around.

It looks like this post/question has many views, but no up2date solutions, so I post it here.

Please criticize my solution, I am definitely not a Windows guy, I am on the Linux side, so had to learn things digging into this.


My goals reached: I can simply press computer reset button, after Windows 10 boots, I get wsl Ubuntu Linux automatically started in background, with running services accessible via network through Windows box IP.

So I got 2 following things explained here:

  1. How to configure Windows 10 startup properly, adding my script into Windows startup.
  2. Commands from my script explained:
    • start Ubuntu Linux wsl2;
    • configure access to Linux services from network;
    • make Linux services running in background.

Configuring Startup

I use Task Scheduler to start it at system startup, in this way it start even before anybody is logged into Windows. I have the task created in the following way:

  • General:
    • Run whether user is logged on or not;
    • Run with highest privileges;
  • Triggers:
    • At system startup;
  • Actions:
    • Start a program:
      • Program/script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • Add arguments: C:\Users\MY-USERNAME\bin\UStartup.cmd

The script

The full path is C:\Users\MY-USERNAME\bin\UStartup.cmd

You can change it, just make sure that configuring startup using Task Scheduler, you specify the correct path there.

The script does the following things:

  • it starts Ubuntu Linux wsl2 at Windows startup even before anybody logs into computer;
  • it automatically detects Ubuntu Linux internal virtual IP and configures forwarding of pre-defined by you ports, so you can access Linux services from network going to Windows box external IP (for example you can log into linux via ssh and/or access linux web server via web from local network);
  • it forces Ubuntu Linux wsl2 working in background, even if you do not have any wsl console open.

The full script

echo %date%-%time% > %TEMP%\UStartup.log 2>&1

PowerShell -Command "Set-ExecutionPolicy Unrestricted" >> %TEMP%\UStartup.log 2>&1

PowerShell wsl -d Ubuntu "/usr/bin/sudo /root/startup.sh" >$null 2>&1
PowerShell wsl -l -v >> %TEMP%\UStartup.log 2>&1

PowerShell "$A = (wsl -- sh -c 'hostname -I | cut -d\  -f1'); echo \"[$A]\"; netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$A; netsh interface portproxy show all" >> %TEMP%\UStartup.log 2>&1

Start "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Windows PowerShell" wsl tmux new-session -s test >> %TEMP%\UStartup.log 2>&1
PowerShell sleep 3 >> %TEMP%\UStartup.log 2>&1
PowerShell "$A = (tasklist|Select-String 'wsl.exe'|Select-String 'Console'|%%{($_ -split '\s+')[1]}); echo \"[$A]\"; Stop-Process -Id $A" >> %TEMP%\UStartup.log 2>&1

echo %date%-%time% >> %TEMP%\UStartup.log 2>&1

Hope it may help to somebody!



Now script commands quick explanations, part by part

PART 1: Starting wsl2 Ubuntu Linux

1.1. Clear log file and print date/time to the log file (not needed for functionality, it is just nice to have date/time in log):

echo %date%-%time% > %TEMP%\UStartup.log 2>&1

1.2. Change Execution Policies to get rid of restrictions:

PowerShell -Command "Set-ExecutionPolicy Unrestricted" >> %TEMP%\UStartup.log 2>&1

1.3. Start wsl:

PowerShell wsl -d Ubuntu "/usr/bin/sudo /root/startup.sh" >$null 2>&1

I configured inside of Linux sudo, to allow execute "sudo /root/startup.sh" as root to start cron service and also some extra services, but it is up to you, you can skip sudo part and simply just start wsl. Also I redirected output to /dev/null, because it looks ugly, anyway it is logged inside of Linux wsl.

1.4. Print list of all wsl systems I got on my box, Ubuntu should be shown as running there, because it was just started (not needed for functionality, it is just nice to have extra info in log):

PowerShell wsl -l -v >> %TEMP%\UStartup.log 2>&1

PART 2: Configuring ports redirect from Windows IP to wsl2 Ubuntu Linux

2.1. Retrieve currently assigned Ubuntu Linux wsl2 IP address and get Windows box port forwarding configured, also print current ports forwarding info:

PowerShell "$A = (wsl -- sh -c 'hostname -I | cut -d\  -f1'); echo \"[$A]\"; netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$A; netsh interface portproxy show all" >> %TEMP%\UStartup.log 2>&1
echo %date%-%time% >> %TEMP%\UStartup.log 2>&1

In this example I have the following configured: if somebody opens in his browser my windows box ip port 8080, he is going to access Ubuntu Linux wsl2 box port 8080.

If you have Linux sshd enabled and want it accessible, add another such a line with '22' instead of '8080' in 2 places replaced.

PART 2: Making wsl2 Ubuntu Linux services running in background

3.1. Start wsl session with a new tmux console session open:

Start "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Windows PowerShell" wsl tmux new-session -s test >> %TEMP%\UStartup.log 2>&1

Actually it opens a new "Windows Powershell" window with active wsl console on your screen. You cannot see it, if you not logged in, but if you run the script manually (do not forget about elevated privileges), you will see it.

3.2. Wait for 3 seconds (command #1) and after kill "Windows Powershell" window (command #2), leaving tmux console session active:

PowerShell sleep 3 >> %TEMP%\UStartup.log 2>&1

PowerShell "$A = (tasklist|Select-String 'wsl.exe'|Select-String 'Console'|%%{($_ -split '\s+')[1]}); echo \"[$A]\"; Stop-Process -Id $A" >> %TEMP%\UStartup.log 2>&1

We search for process 'wsl.exe' for "Windows Powershell", there should be no others such processes during system startup. But if you run it manually - be aware: you do not want another wsl.exe have running.

3.3. Print date/time to log file (not needed for functionality, it is just nice to have date/time in log):

echo %date%-%time% >> %TEMP%\UStartup.log 2>&1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .