Fan command from a Fantom process Failing - cmd

I am trying to call a fan command from a Fantom process. This is the error it runs from Fantom, it runs correctly from the command line. What do you think the problem could be?
class Main {
Void main() {
buf := Buf()
Process() {
command = ["fan <pod>::<type>.<method>"]
out = buf.out
}.run.join
outStr := buf.flip.readAllStr
}
}
This is the error I am getting:
sys::IOErr: java.io.IOException: Cannot run program "fan <pod>::<type>.<method>":
CreateProcess error=2, The system cannot find the file specified
java.lang.ProcessBuilder.start (Unknown)
fan.sys.Process.run (Process.java:141)
PDFCommandLine::Main.main (Main.fan:10)
java.lang.reflect.Method.invoke (Unknown)
fan.sys.Method.invoke (Method.java:559)
fan.sys.Method$MethodFunc.callOn (Method.java:230)
fan.sys.Method.callOn (Method.java:139)
fanx.tools.Fan.callMain (Fan.java:185)
fanx.tools.Fan.executeType (Fan.java:147)
fanx.tools.Fan.execute (Fan.java:41)
fanx.tools.Fan.run (Fan.java:308)
fanx.tools.Fan.main (Fan.java:346)

I think the issue is that there is no Windows file called fan, only fan.bat!
It is only the Windows command prompt that interpolates fan and looks for executable extensions, .com, .bat, .cmd, .exe, etc...
Note experience with the BedSheet proxy tells me that the new fan.bat launches Java in separate process and the batch file finishes straight away; so you don't actually receive any output from the Process class, even though the Fantom program ran successfully.
Instead you need to compose a command that launches the Java process yourself. Something like:
C:\> java -cp %FAN_HOME%\lib\java\sys.jar fanx.tools.Fan <pod>::<type>.<method>
Here's a little snippet that does just that in a cross-plaform manner:
static Process fanProcess(Str[] cmd) {
homeDir := Env.cur.homeDir.normalize
classpath := (homeDir + `lib/java/sys.jar`).osPath
args := ["java", "-cp", classpath, "-Dfan.home=${homeDir.osPath}", "fanx.tools.Fan"].addAll(cmd)
return Process(args)
}
And you can use like this:
buf := Buf()
fanProcess(["<pod>::<type>.<method>", "arg1", "arg2"]) {
out = buf.out
}.run.join
outStr := buf.flip.readAllStr
Note the above requires java to be on your PATH.

Related

"The process cannot access the file because it is being used by another process" when creating files in a temporary directory

I consistently get the following error on Windows (both local and inside Github Actions). I do not get this error on macOS and Linux.
Error: Custom { kind: Other, error: PathError { path: "C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\.tmpi45reT", err: Os { code: 32, kind: Other, message: "The process cannot access the file because it is being used by another process." } } }
The code is basically creating a temporary dir with tempfile crate and then doing a bunch of File::creates in that directory (and some other, probably irrelevant stuff). After all writes each file is explicitly dropped and the dir handle is explicitly closed. Here's the rough version of the code:
Rust playground
use fs::File;
use std::env;
use std::fs;
use std::io::Write as _;
use tempfile::tempdir;
fn main() -> std::io::Result<()> {
let dir = tempdir()?;
let dir_path = dir.path();
let pkg_path = dir_path.join("package.json");
let mut pkg_json = File::create(pkg_path)?;
pkg_json.write_all(
r#"
{
"name": "testpackage"
}
"#
.as_bytes(),
)?;
let _a = File::create(dir_path.join("yarn.lock"))?;
let _b = File::create(dir_path.join(".gitignore"))?;
env::set_current_dir(&dir)?;
drop(pkg_json);
drop(_a);
drop(_b);
dir.close()?;
Ok(())
}
This is a part of integration tests, where each test does something similar to the code above.
What could be causing this error?
I found that the reason was not changing the current directory back to where it was after manually setting it to the tmp directory.

Args and passing args from position[1] (not position 0)

I am reading a command line string from a config file (config.json) :
"execmd" : "c:\\windows\\system32\\cmd.exe /c runscript.cmd"
I want to pass this to exec.Command() - but this function requires 2 parameters:
exec.Comm*emphasized text*and (cmd, args...)
Where cmd is the first segment (cmd.exe) and args would then be every space deliminated value thereafter.
I am not exactly sure if I need to read the config string, and then manually split it up in an array for each space deliminator? Is there any way of converting a string into args easily?
How would it be possible to do something like this, where I can refer args... from an index? (the below code doesn't work, can't refer args this way)
exec.Command (arg[0], args[1]...)
If the values coming it from the config file are in a format executable by shell, you're going to run into a host of problems just splitting on spaces (e.g. quoted arguments containing spaces). If you want to take in a command line that would be executable in a shell, you're going to want to have a shell execute it:
exec.Command("cmd.exe", "/c", execmd)
There is no way of "converting a string into args" because it varies from shell to shell.
I found this thread regarding the issue : https://groups.google.com/forum/#!topic/golang-nuts/pNwqLyfl2co
Edit 1: ( didnt work )
And saw this one using RegExp - and found out how i can index into the args also :
r := regexp.MustCompile("'.+'|\".+\"|\\S+")
s := appConfig.JobExec.ExecProcess
m := r.FindAllString(s, -1)
Exec.Command (m[0],m[1:])
This seems to work also with quoted strings !
Edit 2:
It didnt work and didnt work on windows, theres an issue with params being passed on when running with cmd. Using sysprocattr.cmdline will make it work :
cmdins := exec.Command(cmd)
cmdins.SysProcAttr = &syscall.SysProcAttr{}
for _, arg := range args {
cmdins.SysProcAttr.CmdLine = cmdins.SysProcAttr.CmdLine + arg + " "
}
the issue described here :
https://github.com/golang/go/issues/17149

Implementing my shell using c, Change the directory

Implementing my own shell
I did not post the whole code to save your time,
briefly i used exec() to execute the command lines
my problem is clarified in a comment line below
thanks for any help
int main()
{
int pid[2];
char inbuf[10];
printf("\n\nSimple Shell using C\n");
char hostname[1024];
hostname[1023] = '\0';
gethostname(hostname, 1023);
char resolved_path[100];
realpath("./", resolved_path);
printf("Maram #%s :<%s>$ ", hostname,resolved_path); //MY PROBLEM: For cd command ex: cd Desktop/Folder.. does not go in this directory and the resolved path does not change
while(1){
printf("[my shell] :");
gets(inbuf);
if(fork()){
wait();
}else{
pip(inbuf, 0, 1);
}
}
return 0;
}
/// ALSO, how can I print a command not found if an entered command line is not correct?
Nowhere in this program do you call chdir() -- which is the only way to change your current process's directory (barring functional equivalents such as fchdir(); which still retain the fundamental restriction that they impact only the current process and not its parents).
Using an exec-family call to launch an external program which changes its own directory has no effect -- that program's directory is separate from your shell's. The shell must change its own directory without forking if you want to have any effect.

D: executeShell on Windows to run another program not returning immediately

I'm using D as a scripting language for Windows 7 console stuff to automate boring tasks. One of my scripts (open.exe) is supposed to allow me to open stuff from the command line without me having to specify which program I use (I have a configuration file with this stuff). Now, I use executeShell to do this, and call something like start [name of program I want to use] [name of input file]. If I do this directly from the shell, it returns immediately, but if I do it using my D script, it doesn't return until the program that it opens is closed. What should I do to allow it to return immediately?
For reference purposes, this is the business logic of my script (the main method just does some argument parsing for piping purposes):
immutable path = "some//path//going//to//config//file.conf";
void process(string input) {
string extension = split(input,".")[1]; //get file extension from input
auto config = File(path,"r"); auto found = false;
while (!config.eof()){
auto line = chomp(config.readln());
if (line[0]!='#') { //skip comment lines
auto divided = split(line, ":");
if (divided[0] == extension) {
found = true;
auto command = "start " ~ divided[1] ~ " " ~ input;
auto result = executeShell(command);
//test for error code and output if necessary
writeln(result.output);
}
}
}
if (!found)
writeln("ERROR: Don't know how to open " ~ input);
}
From the top of the std.process documentation:
Execute and wait for completion, collect output - executeShell
The Windows start program spawns a process and exits immediately. D's executeShell does something else. If you'd like to spawn another program, use the appropriate functions: spawnProcess or spawnShell.

Read Windows Command Prompt STDOUT

I have a command line application that runs on a windows server. The command prompt remains open when the program is running, and log messages are output to the command prompt window as the program functions.
My need is to read the messages that appear on the command prompt as the program runs, and then run particular commands if a specific set of words appear in the messages.
What's the easiest way to do this on a windows machine? (without modifying the app)
Reading those two posts will give you the solution:
ProcessStartInfo
Capturing console output.
The idea is to to run your app (not modifying it) from your new app (written in C#) and redirect its input-output here, reading and writing as you please.
An example could be:
Process proc;
void RunApp()
{
proc = new Process();
proc.StartInfo.FileName = "your_app.exe";
proc.StartInfo.Arguments = ""; // If needed
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.OutputDataReceived += new DataReceivedEventHandler(InterProcOutputHandler);
proc.Start();
proc.WaitForExit();
}
void InterProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
// Read data here
...
// Send command if necessary
proc.StandardInput.WriteLine("your_command");
}

Resources