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

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

Related

cmd converts em-dash to hyphen on pasting. Any workaround?

I want to be able to paste file paths into cmd with em dashes (—, alt 0151) in them.
cmd converts them to ones where the em dashes have been replaced by a hyphen.
Manual input:
(Keyboard) D:\—\image.png
(cmd) D:\—\image.png
Entering this would open the file as expected.
Pasted input:
(Clipboard) D:\—\image.png
(cmd) D:\-\image.png
Entering this would give me an error because a directory named hyphen doesn't exist.
This is baffling because the file system supports paths to have such a character - I can access this file if I type the path manually, and programs can open it just fine.
Why convert a character that is supported? If it wasn't supported when the conversion was added, why not remove the conversion when the support was added?
More importantly, how can I work around this while keeping the em dashes? I have programs that depend on such paths and it'd be inconvenient to change them in all of them.
Similar to:
How to deal with an em dash in a filename
Using “En Dash” in an input file to a batch file
Rename Files having EmDash using a Batch File
Changing the code page made no difference.
My workaround was to create an AutoHotKey script to parse the path being pasted and to send alt 0151 whenever it encounters an em dash.
It could be faster, but it works - which is miles better than receiving an error.
#SingleInstance, force
numpad7::
tooltip, exited!
Clipboard := stored
sleep, 300
exitapp
#IfWinActive ahk_exe cmd.exe
$^v::
cliptext := clipboard
stored := ClipboardAll
StringGetPos, garbage, cliptext, —
garbage =
if !ErrorLevel {
Loop, Parse, cliptext
{
char = %A_LoopField%
If (char == "—") {
clipboard := sentence
sendinput, ^v
SendInput {alt down}{numpad0}{numpad1}{numpad5}{numpad1}{alt up}
sentence := ""
} else if (char == "") {
char := " "
gosub define_sentence
} else {
gosub define_sentence
}
}
; send sentence when EOL
gosub define_sentence
Clipboard := sentence := SubStr(sentence, 1, -1)
sendinput, ^v
sleep 200
Clipboard := stored
stored =
sentence =
return
} else {
sendinput, ^v
}
return
define_sentence:
sentence := sentence . char
tool := "s= " . sentence . "`n" . "c= " . char
tooltip, %tool%
return
On another note, the highlight.js for autohotkey doesn't seem to work which is great.

Fan command from a Fantom process Failing

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.

JScript: identifying whether double quotes are passed to a WSH script

There are situations when it is important to identify whether double quotes are passed as arguments to a WSH script. For example because they should be passed to another executable to be run.
The standard parsing functions/objects:
objArgs = WScript.Arguments;
for (i = 0; i < objArgs.length; i++)
{
WScript.Echo(objArgs(i));
}
do not differentiate between:
cscript foo.js "bar"
and
cscript foo.js bar
Is it possible with some other approach?
Note: I also tried to sort of escape them with several combinations like:
cscript foo.js '"bar"'
It seems that they are simply stripped away.
Following #Ekkehard.Horner suggestions:
Solution
// parseArgs.js
// Parsing jscript script arguments verbatim
var Shell = new ActiveXObject("WScript.Shell"),
wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2"),
guid = (new ActiveXObject("Scriptlet.TypeLib")).GUID.substring(0,38),
windir=Shell.ExpandEnvironmentStrings("%WinDir%"),
winver="\"" + windir + "\\System32\\winver.exe\" " + guid,
pcol, pid, cmd;
// Run winver.exe hidden and get this script ID as its ParentProcessId
winver=winver.replace(/\\/g, "\\\\");
Shell.Run("winver " + guid, 0);
pcol = new Enumerator (wmi.ExecQuery(
"SELECT * From Win32_Process WHERE CommandLine='"+ winver + "'",
"WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
var prc = pcol.item();
pid=prc.ParentProcessId;
prc.Terminate;
}
// Get the command line for the found PID
pcol = new Enumerator (wmi.ExecQuery(
"SELECT * From Win32_Process WHERE ProcessID="+ pid,
"WQL", 32));
for (; !pcol.atEnd(); pcol.moveNext()){
var prc = pcol.item();
cmd =prc.CommandLine;
}
WScript.Echo(cmd);
// Parse command line for arguments
var ags,
parseCmd=function(cmd){// WMI trims initial spaces
var p = new Object(),
re =/^"/.test(cmd) ? /"[^"]+" */ : /\S+\s*/;
p.nxt=re.test(cmd) ? cmd.match(re)[0] : ""; // extract next token
p.rst=cmd.replace(re, "") ; // remainder
return(p);
}
// Strip c/wscript path
ags=parseCmd(cmd).rst
//WScript.Echo(ags);
// Remove WSH "//xxx" options
ags=ags.replace(/\/\/\w+ +/g, "")
//WScript.Echo(ags);
// Strip script name and get arguments
ags=parseCmd(ags).rst
WScript.Echo(ags);
// Loop args and store as an array
var i=1, aags=[];
while(ags != ""){
var p =parseCmd(ags);
ags=p.rst;
aags.push(p.nxt.replace(/ +$/, ""));
WScript.Echo(i, p.nxt);
i++;
}
WScript.Echo(aags);
Test
Running parseArgs.js gives:
> cscript //nologo parseArgs.js "hello" world
cscript //nologo parseArgs.js "hello" world
"hello" world
1 "hello"
2 world
"hello",world
The line:
> parseArgs.js "hello" world
gives similar results.
Comments
Do we need such a convoluted script? Short answer: no. Long: depends.
In general, assuming you know the name of your script when it is run, you could query WMI for it.
Anyway, when you deploy your script, you do not normally have control on the deploy directory. So, if there is another script running under the same name, you can't know for sure which one is yours.
Another not so edge case is when there are two or more instances of your script running.
The strategy here is to run some dummy standard Windows executable (winver.exe) hidden, passing to it a GUID. In this way, it is safe to identify winver.exe command line by the unique GUID and consequently your script as the parent of winver.exe.
winver.exe does not require arguments, but does not protest if you pass some to it.

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.

Difficulties understanding the mechanism of retrieving parameter values passed to a script in a function

The script passes two parameter values to another instance of the script. So the built-in parameter variable, 0, contains the number of passed parameters. 1 is in the below example "C:/Windows" and 2 is "/switchtest"
It is possible to assign the parameter values to strParam1 and strParam2 with the traditional method outside the function (with the single equal sign). However, inside a function, the assignments fail.
If they are assigned in a loop with the := sign, it seems to work.
Why is it? Can anybody explain this behavior?
strParam1 = %1%
strParam2 = %2%
msgbox, 64, Outside the Function, number of parameters:%0%`npath: %strParam1%`nswitch: %strParam2%
test_params()
strPath := "C:/Windows"
strSwitch := "/switchtest"
RunWait "%A_AhkPath%" "%A_ScriptFullPath%" "%strPath%" "%strSwitch%"
test_params() {
global 0
; this works
; loop %0%
; strParam%A_Index% := %A_Index%
; this causes an error: "This dynamic variable is blank. If this variable was not intended to be dynamic, remove the % symbols from it."
; strParam1 := %1%
; strParam2 := %2%
; this passes empty values; however, this method works outside the function.
strParam1 = %1%
strParam2 = %2%
msgbox, 64, Inside the Function, number of parameters:%0%`npath: %strParam1%`nswitch: %strParam2%
if strParam2
exitapp
}
You had the right idea with global 0; that allows %0% to carry into the function from toplevel. You just need to declare global 1, 2 as well.
Even if you do this, you can't use := to assign them to variables, because := deals with expressions and there is no syntax to use them in expressions (normally a variable is referred to in an expression with the variable name alone, without %%; obviously 1 and 2 are interpreted as actual numbers instead of variables).
#echristopherson answered the question, but I'd like to propose a workaround. This assumes you're using AutoHotkey_L.
If you run the test script with the args "a b c", it gives you this.
3
1, a
2, b
3, c
The test:
argv := args()
test := argv.MaxIndex() "`n"
for index,param in argv
test .= index ", " param "`n"
MsgBox % test
And the function:
args() {
global
local _tmp, _out
_out := []
Loop %0% {
_tmp := %A_Index%
if _tmp
_out.Insert(_tmp)
}
return _out
}

Resources