I'm wondering if it's possible to run a program in WSL from an Electron application on Windows.
The situation is that I have a binary which only runs on *nix systems that I want to execute in the background of my desktop Electron app. The binary itself can be included as part of the Electron package, so I know it will be available.
I'm aware WSL isn't always installed on Windows systems, but in cases where it is, would it be possible to somehow "call into it" to run the binary?
I tried researching the question, but have only been able to find information about running Electron itself from inside WSL which is not what I'm looking for.
I'm honestly not familiar with Electron, but it shouldn't be a problem at all. WSL itself can be launched as a standard Windows .exe, which can then specify the Linux application to run on the command-line. The results are returned to stdout.
For instance:
const { execFile} = require('child_process');
execFile('wsl.exe', ['--exec','ls','-la'], {}, (err, stdout, stderr) => {
console.log(err);
console.log(stdout);
console.log(stderr)
});
The --exec specifies the binary to run. This isn't strictly necessary, but it avoids the overhead of running a standard shell (e.g. Bash) and then having Bash execute the binary.
The ls is, of course, the binary. As to the path of your binary that is packaged in the Electron, that's where I'd stumble a bit. That will be a Windows path (although exactly where it is bundled in the Electron app, I don't know. Regardless, you'll need to convert it to the equivalent WSL/Linux path using the wslpath command.
For instance, similar to the above (and without any real error handling):
const { execFile} = require('child_process');
execFile('wsl.exe', ['--exec','wslpath','C:\\Windows\\notepad.exe'], {}, (err, stdout, stderr) => {
console.log(err);
console.log(stdout);
console.log(stderr)
});
stdout will contain /mnt/c/Windows/notepad.exe.
Note that, if your Linux application requires and environment variables to be pre-configured, that will need to be done through the WSLENV variable.
Related
My OS is windows. I want to create a bash shell using xterm and node-pty. There is this line of code:
this.shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash';
using which the terminal is rendered in browser. Now suppose if I write:
this.shell = os.platform() === 'win32' ? 'bash' : 'powershell.exe';
or
this.shell = os.platform() === 'linux' ? 'powershell.exe' : 'bash';
why isn't the bash shell getting rendered. It is giving me this error:
D:\DE_proj\linuxterminal\server\node_modules\node-pty\lib\windowsPtyAgent.js:75
term = this._ptyNative.startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor);
^
Error: File not found:
at new WindowsPtyAgent (D:\DE_proj\linuxterminal\server\node_modules\node-pty\lib\windowsPtyAgent.js:75:36)
at new WindowsTerminal (D:\DE_proj\linuxterminal\server\node_modules\node-pty\lib\windowsTerminal.js:50:24)
at Object.spawn (D:\DE_proj\linuxterminal\server\node_modules\node-pty\lib\index.js:28:12)
at PTY.startPtyProcess (D:\DE_proj\linuxterminal\server\PTYService.js:16:27)
at new PTY (D:\DE_proj\linuxterminal\server\PTYService.js:11:10)
at Namespace.<anonymous> (D:\DE_proj\linuxterminal\server\SocketService.js:40:18)
at Namespace.emit (events.js:315:20)
at Namespace.emitReserved (D:\DE_proj\linuxterminal\server\node_modules\socket.io\dist\typed-events.js:56:22)
at D:\DE_proj\linuxterminal\server\node_modules\socket.io\dist\namespace.js:140:26
at processTicksAndRejections (internal/process/task_queues.js:75:11)
[nodemon] app crashed - waiting for file changes before starting...
Sorry for any mistakes in the question I am quite new to this...
Bash will not run on windows, unless you either use a virtual environment such as minTTY (if you have Git bash, you have a fully functional bash shell minus a couple features), or you install WSL2.
Linux and windows run different types of programs. Bash was built for Linux.
Windows only runs file types such as .exe, .bat, .dll, etc. Bash doesn't have a file type. Their are a few 3rd party ports available, but ever since Windows released WSL2, which creates Linux shells inside Windows 10, their isn't much of a demand.
Your best bet is to enable WSL2 on your machine, or else download git Bash.
I need to emulate a terminal in go. I try to do it like this:
lsCmd := exec.Command("bash", "-c", "ls")
lsOut, err := lsCmd.Output()
if err != nil {
panic(err)
}
fmt.Println(string(lsOut))
And it seems to work correctly (the native ubuntu terminal displays a horizontal list, and the result of this function goes vertically).
But if I specifically call the wrong command, for example exec.Command ("bash", "-c", "lss"), I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
I need to communicate with the native terminal, and get the same thing as the result of the command if I wrote the command in the standard ubuntu terminal.
What is the best way to do this? Maybe the exec library is not suitable for this? All this is necessary for front-end communication with the OS terminal. On a simple html/css/js page, the user enters a command, after go it sends it to the native terminal of the operating system and returns the result to the front-end.
How I can get the same result of executing commands as if I were working in a native terminal?
The problem
But if I specifically call the wrong command, for example exec.Command
("bash", "-c", "lss"), I get:
panic: exit status 127
And in the native ubuntu terminal I get the following result:
Command 'lss' not found, did you mean:
and enumeration of commands.
This has nothing to do with Go, and the problem is actually two-fold:
Ubuntu ships with a special package, command-not-found, which is usually preinstalled, which tries make terminal more friently for mere mortals by employing two techniques:
It tries to suggest corrections for misspellings (your case).
It tries to suggest packages to install when the user tries to execute a program which would have been be available if the user had a specific package installed.
When the command is not found, "plain" (see below) shell fails the attempt by returning a non-zero exit code.
This is absolutely expected and normal.
I mean, panicking on it is absolutely unwise.
There's a historical difference on how a shell is run on a Unix system.
When a user logs into the system (remember that back in the days the concept of the shell was invented you'd be logging in via a hardware computer terminal which was basically what your GNOME Terminal window is but in hardware, and connected over a wire),
the so-called login shell is started.
The primary idea of a logic shell is to provide interactive environment for the user.
But as you surely know, shells are also capable of executing scripts.
When a shell executes a script, it's running in a non-interactive mode.
The modes a Unix shell can work in
Now let's dig deeper into that thing about interactive vs non-interactive shells.
In the interactive mode:
The shell is usually connected to a real terminal (either hadrware or a terminal emulator; your GNOME Terminal window is a terminal emulator).
"Connected" means that the shell's standard I/O streams are connected to the terminal, so that what the shell prints is displayed by the terminal.
It enables certain bells and whistles for the user, usually providind limited means for editing what is being input (bash, for instance, engages GNU readline.
In the non-interactive mode:
The shell's standard I/O streams are connected to some files (or to "nowhere" — like /dev/null).
No bells and whistles are enabled — as there is nobody to make use of them.
GNU bash is able to run in both modes, and which mode it runs in depends
on how it was invoked.
When initializing in different modes, bash reads different initialization scripts, and this explains why the machinery provided by the command-not-found package gets engaged in the interactive mode and does not when bash is run otherwise — like in your invocation from Go.
What do do about the problem
The simplest thing to try is to run bash with the --login command-line option or otherwise make it think it runs as an interactive shell.
This might solve the problem for your case but not necessarily.
The next possible problem is that some programs do really check whether they're running at a terminal — usually these are programs which insist on real interaction with the user, usually for security purposes, and there are programs which simply cannot run when not connected to a real terminal — these are "full-screen" text UI programs such as GNU Midnight Commander, Vim, Emacs, GNU Nano and anything like this.
To solve this problem, the only solution is to run the shell in a pseudo-terminal environment, and that's what #eudore hinted at in their comment.
The github.com/creack/pty might be a package to start looking at; golang.org/x/crypto/ssh also provides some means to wrangle PTYs.
I am creating an installation using InstallShield 2018 in windows 10. I need to execute a script file (.sh) in Ubuntu from a function in Installscript. I tried the following but it did not work:
szCmdPath = "C:\\Users\\Admin\\AppData\\Local\\Microsoft\\WindowsApps\\Ubuntu.exe";
szCmdLine = ". /mnt/d/test.sh";
LaunchAppAndWait( szCmdPath, szCmdLine, WAIT);
However I can execute the exact same file in Ubuntu Terminal and it works great. I did turn on Window sub system for Linux and install Ubuntu on windows. Why is this happening? Why can I run cmd.exe from installscript but not Ubuntu?
Thank you in advance.
EDIT 1: if I pass an empty string as parameter, Ubuntu is start and waits for my input commands. But when I pass the path to my script file, nothing happened except a flash of the terminal console before my installation goes on running.
From my reading, wsl and ubuntu differ slightly. It looks like wsl is a bit magical and occasionally similar to bash -c or ubuntu -c, whereas you can consider Ubuntu.exe as somewhat equivalent to /bin/bash.
If you try to run /bin/bash . /mnt/d/test.sh from a bash prompt, things don't go well. So the correct approach will depend on the contents of your script and what you need to happen. I think one of the following options are the most likely:
remove . from your command; instead run ...\Ubuntu.exe /mnt/d/test.sh
add -c to your command; instead run ...\Ubuntu.exe -c . /mnt/d/test.sh
Note that %LOCALAPPDATA%\Microsoft\WindowsApps\Ubuntu.exe is a special file (zero bytes), so it's also plausible that it needs some special handling. For instance, maybe it requires a 64-bit caller. If that's the case, you may need to wrap it in a call through a 64-bit cmd prompt. My quick tests don't show this as likely, however, so I think it will work without this extra indirection.
When AppleScripts are run through the system-wide Script Menu, their progress is displayed in the menu bar using the ScriptMonitor applet (located in /System/Library/CoreServices/ScriptMonitor.app, introduced in OS X Yosemite). This allows you to stay aware of running scripts, monitor progress, and easily stop running scripts.
Is it possible to run AppleScripts through the ScriptMonitor from outside the Script Menu, for example from Terminal or from system calls from other applications?
I have tried various permutations of the following commands, all without success:
/System/Library/CoreServices/ScriptMonitor.app/Contents/MacOS/ScriptMonitor /PATH/TO/SCRIPT
or
open -a /System/Library/CoreServices/ScriptMonitor.app --args /PATH/TO/SCRIPT
The reason this would be useful is that there are many helper applications that run AppleScripts in response to events, but tend not to be very good at notifying the user about their success or failure.
So, it turns out this can be done using NSUserScriptTask from the Cocoa frameworks, either as part of a compiled command-line application or through AppleScript/Objective-C (ASObjC).
This solution allows AppleScripts, Automator workflows, and shell scripts to be run from the System ScriptMonitor.app utility.
ASObjC Solution
This handler will run natively on OS X 10.10 Yosemite and later. It takes a single parameter, a string containing the POSIX-style (slash-delimited) path to the script. The script is executed immediately in the background, and no result is returned.
use framework "Foundation"
to runInScriptMonitor(script_path)
set {script_task, url_error} to current application's NSUserScriptTask's alloc()'s initWithURL:(script_path as POSIX file) |error|:(reference)
if url_error is not missing value then error (url_error's localizedDescription as string) number (url_error's code as integer)
script_task's executeWithCompletionHandler:(missing value)
# The following delay was increased due to a system hang on Mojave after installation of Security Update 2020-004 (previously, the delay was 0.05).
delay 10 -- Necessary to allow NSUserScriptTask to be dispatched before execution terminates.
return
end runInScriptMonitor
It is called as follows: runInScriptMonitor("/PATH/TO/FILE")
This allows you to run scripts in ScriptMonitor from within AppleScript. If the call is placed in a wrapper AppleScript, the wrapper script can then be called from the command line using osascript.
Compiled Objective-C Solution
Follow these instructions to create a command-line program that takes the script path as input and runs the script using ScriptMonitor. You must have the Xcode Command Line Tools (or the full Xcode) installed in order to compile the code.
Save the following code as osascriptmonitor.m in the Desktop folder:
#import <Foundation/Foundation.h>
int main(int argc, const char *argv[]) {
if (argc < 2) {
printf("usage: osascriptmonitor /path/to/script\n");
} else {
NSString *script_path = [NSString stringWithUTF8String:argv[1]];
NSUserScriptTask *script_task = [[NSUserScriptTask alloc] initWithURL:[NSURL fileURLWithPath:script_path] error:nil];
[script_task executeWithCompletionHandler:nil];
[NSThread sleepForTimeInterval:60.0f];
}
return 0;
}
Compile the program by running the following commands from Terminal:
cd ~/Desktop
gcc -framework Foundation osascriptmonitor.m -o osascriptmonitor
You will now have an executable file named osascriptmonitor on your Desktop. You can run that program from Terminal, passing the path of the script that you want to run in ScriptMonitor.
Example (replace /PATH/TO/SCRIPT with the path of the script you want to run):
~/Desktop/osascriptmonitor "/PATH/TO/SCRIPT"
If you then move the executable file to /usr/local/bin, you can run the program without having to specify its entire path.
osascriptmonitor "/PATH/TO/SCRIPT"
Edit Jan 3, 2020:
Direct Solution
By happy accident, I stumbled across an undocumented option for osascript that makes the above largely unnecessary for AppleScripts: the -P switch.
Usage: osascript -P "/PATH/TO/SCRIPT"
On its own, this will make the script appear in the menu only if Script Monitor is already running. Script Monitor can be launched ahead of time (or while the script is running) and will quit automatically once all scripts have finished.
The best way to launch Script Monitor is using the -g option:
open -g /System/Library/CoreServices/ScriptMonitor.app
Using this method, it is possible to easily pass arguments to the script in addition to having it appear in Script Monitor.
I installed amazonCLI, as well as Cygwin, and changed the Path env variable to:
%SystemRoot%\System32\Wbem;C:\Program Files\Prio;C:\Program
Files\Diskeeper Corporation\ExpressCache\;C:\Program
Files\Amazon\AWSCLI\;C:\cygwin64\bin
When I open the command prompt, first of all it is directly pointing to
C:\Users\Stephane
(which I think is weird). And then when I input something like 'ls', the return error is:
'ls' is not recognized as internal or external command, operable program or batch file
Can you please help me know what I am doing wrong?
Thanks
From the error message you got, it's evident, that you are trying to execute Cygwin's commands from the Windows Console. That's also the reason, why it's executed in your Windows' profile directory instead of the Cygwin's one.
You should read the basics about using the Cygwin first, because it seems you don't know what Cygwin exactly is and how to use it. Maybe you don't need the Cygwin at all, it depends on what you need to accomplish. The is for example GnuWin tool set or UnxUtils, which are just a sets of standalone GNU tools compiled for Windows. Cygwin is more like system than standalone utilities. You can for example execute shell scripts under the Cygwin.
So it depends on your needs. But I simply can't imagine using Windows without Cygwin yet.
You can execute Cygwin's shell from the Windows Console, but I advice you to use MinTTY (which is in directory $CYGWIN_DIR/bin). MinTTY is a terminal emulator, which executes your Cygwin shell (bash by default).
To execute for example bash directly from the Windows Console, just execute $CYGWIN_DIR/bash.exe --login -i.