Is it possible to run WSL Bash in non-interactive mode?

1.4k views Asked by At

One may want to use Bash on Windows in Task Scheduler or maybe as version-control hook scripts. Is it possible or supported?

If not, why? Is it a bug or a measure to prevent some issues?

2

There are 2 answers

0
3D1T0R On

If I'm understanding your question correctly, the -c option is what you're looking for. It allows you to directly invoke a Linux command.
For example, to open the man page for bash (perhaps in order to find out about the -c option):

bash -c "man bash"

Note: You can leave off the quotes if you escape any spaces (e.g. bash -c man\ bash), but it's often easier to just use the quotes, as the first unescaped space will lose the rest of your command.
e.g. bash -c man bash will be interpreted the same as bash -c man.

0
nar8789 On

Use @3d1t0r's solution, but also pipe to cat

wsl bash -c "man bash | cat"  # noninteractive; streams the entire manpage to the terminal
wsl bash -c "man bash"        # shows me the first page, and lets me scroll around; need to hit `q` to exit

If interactive mode is fine, bash -c is often superfluous

wsl man bash                  # same behavior as `wsl bash -c "man bash"`

Context (what in the world is "interactive" vs "non-interactive" invocation?):

The example above might not make it entirely clear, but man is changing its behavior based on what it's connected to.

  • In "interactive mode", man lets me scroll around, so that I can read the page at a comfortable reading pace.
  • In noninteractive mode, man dumps the entire manpage to the console, giving me no time to read anything.

"But wait," I hear you ask, "isn't man catting the man page because you asked it to? I see it right there--man bash | cat"

No, man has no idea what cat is. It just gets hints about whether STDOUT is connected to an interactive terminal.

Here's a different example, that consistently cats:

wsl bash -c "echo hey | grep --color e"       # colors 'e' red
wsl bash -c "echo hey | grep --color e | cat" # colors disappear, what gives?

Now both examples are streaming their output, but the second one is defiantly ignoring my --color flag.

The common thread here is man and grep both behave appropriately depending on whether they think their output is going to be read by a human piped away somewhere.

Other common commands that auto-detect interactivity include ls and git. Usually the behavior change will involve output paging or colors (other variations exist).

  • paging is nice for humans, because humans generally can't read at the speed of streamed output.
  • paging is bad for robots, because paging is a lot of protocol overhead when you can just consume buffered streams. I mean seriously, why are humans so slow and chatty?
  • colors are nice for humans, because we like additional visual cues to aid visual distinction.
  • colors are bad for streaming to file, because your file will be full of ansi color code garbage, that most text editors don't display nicely.

Automatic behavior switching based on whether STDOUT is connected to an interactive terminal makes all these use cases usually "just work".

Restating the Original Question

In my use case and @bahrep's use case, interactive mode can be especially bad for unsupervised scripts (e.g. as launched by Task Scheduler). I am guessing @bahrep's scheduled runs hung on less getting invoked and waiting for human input.

For some reason, wsl-driven scripts launched from the task-scheduler give underlying scripts the wrong hints--they hint that the final output is attached to an interactive terminal.

Ideally, wsl would know from the windows side of the execution environment whether it is getting invoked interactively or not, and pass along the proper hint. Then I could just run wsl [command]. Until that happens, I'll need to use wsl bash -c "[command] | cat" as a workaround.