How can I expand wildcard commandline arguments in Julia?
The shell doesn't seem to expand them before they get there.
If I call my script as julia script.jl *.dat, my output is just *.dat
for arg in ARGS
println(arg)
end
If I write the equivalent program in Java:
public class rejig {
public static void main(String[] args) throws Exception {
for(int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
and call it as java rejig *.dat, I get a listing of all the DAT files in the current directory.
My searching along the lines of "command line", "wildcards", and the like hasn't got me very far.
How do I get Julia to give the same output as the Java code?
I wrote a pure-Julia implementation of Glob (aka fnmatch or wildcard commandline expansion) at https://github.com/vtjnash/Glob.jl, which also available via Pkg.add("Glob").
This can be used for platform-independent wildcard expansion, such as your *.dat example.
As explained in the comment, the shell is the program which expands the wildcards. This expansion is called glob expansion and there are functions in the standard C library which do it (and the shell probably uses itself).
Practically, here is an example of interfacing with the standard libc to expand wildcards:
type GlobType
pathc::Int64
names::Ptr{Ptr{UInt8}}
slots::Int64
extra1::Int64
extra2::Int64
end
function parseglob(gb::GlobType)
i=1
res = UTF8String[]
while i<=gb.pathc
p = unsafe_load(gb.names,i)
if p==C_NULL return res ; end
push!(res,bytestring(p))
i+=1
end
res
end
function glob(filepattern::AbstractString)
gb = GlobType(0,C_NULL,0,0,0)
retval = ccall((:glob,"libc"),Cint,
(Ptr{UInt8},Cint,Ptr{Void},Ptr{GlobType}),
filepattern,0,C_NULL,&gb)
res = ( retval==0 ? parseglob(gb) : UTF8String[] )
ccall((:globfree,"libc"),Void,(Ptr{GlobType},),&gb)
res
end
# glob("*.jl") # ["glob.jl"] on my machine
the library routine has many flags and options which might be of interest to you.
Related
I'm looking for the best possible approach to incorporate a batch file to send arguments to the MFC application rather than relying on the GUI interface. Does anyone know the best method to go about doing this?
I use the following code in my InitInstance method of my app class:
LPWSTR *szArglist = nullptr;
int iNumArgs = 0;
szArglist = CommandLineToArgvW(GetCommandLine(), &iNumArgs);
if (iNumArgs > 0 && szArglist != nullptr)
{
for (int iArg = 0; iArg < iNumArgs; iArg++)
{
CString strArg(szArglist[iArg]);
int iDelim = strArg.Find(_T("="));
if (iDelim != -1)
{
CString strParamName = strArg.Left(iDelim);
CString strParamValue = strArg.Mid(iDelim + 1);
if (strParamName.CollateNoCase(_T("/lang")) == 0)
{
m_strPathLanguageResourceOverride.Format(_T("%sMeetSchedAssist%s.dll"),
(LPCTSTR)GetProgramPath(), (LPCTSTR)strParamValue.MakeUpper());
if (!PathFileExists(m_strPathLanguageResourceOverride))
m_strPathLanguageResourceOverride = _T("");
}
}
}
// Free memory allocated for CommandLineToArgvW arguments.
LocalFree(szArglist);
}
As you can see, I use the CommandLineToArgvW method to extract and process the command line arguments.
A GUI program can receive command line arguments just like a command line program can.
Your Application class (CWinApp, if memory serves) contains a member named m_lpCmdLine that contains the command line arguments (if any) in a CString.
If you also want to deal with shell parameters, you'll probably also want to look at WinApp::ParseCommandLine and CCommandLineInfo (note, if you're dealing with a Wizard-generated program, chances are that WinApp::ParseCommandLine is already being called by default).
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.
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.
I have one string storing hexadecimal data (\xEA\x...). Is it anyway to run that code using vbs? Maybe doing some kind of casting to function pointer or similar.
The C version of what I'm trying to do would be:
unsigned char opcode[] = "\xc0\x...."
main()
{
int (*run)() = (int(*)())opcode;
run();
}
Thank you so much.
You can use function pointers (or function references) with the GetRef function:
dim fp : set fp = GetRef("ShowMessage")
call fp("Woosh")
function ShowMessage(msg)
msgbox msg
end function
To make this work for any string with normally illegal characters for function naming (like the backslash in hexadecimal data) you can use brackets in you function declaration:
dim fp : set fp = GetRef("99 problems")
call fp()
' note: functions normally cannot start with a digit or contain spaces
function [99 problems]()
msgbox "but this aint one"
end function
The only character you cannot use is a closing bracket: ]
Using Perl, how do I capture a single character from STDIN without needing the user to hit enter (similar to C's getch() function)?
Perl has a getc() function, but according to the perlfunc:
However, it cannot be used by itself to fetch
single characters without waiting for
the user to hit enter.
The perlfunc docs do provides a way to read a single character using getc() but it requires manipulating the terminal settings using stty. The script I'm writing needs to work on Windows (without cygwin, msys, etc.) - so that's not an option.
From perlfaq5's answer to How can I read a single character from a file? From the keyboard?
You can use the builtin getc() function for most filehandles, but it won't (easily) work on a terminal device. For STDIN, either use the Term::ReadKey module from CPAN or use the sample code in getc in perlfunc.
If your system supports the portable operating system programming interface (POSIX), you can use the following code, which you'll note turns off echo processing as well.
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1..4) {
my $got;
print "gimme: ";
$got = getone();
print "--> $got\n";
}
exit;
BEGIN {
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
sub cbreak {
$term->setlflag($noecho);
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked {
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW);
}
sub getone {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
}
END { cooked() }
The Term::ReadKey module from CPAN may be easier to use. Recent versions include also support for non-portable systems as well.
use Term::ReadKey;
open(TTY, "</dev/tty");
print "Gimme a char: ";
ReadMode "raw";
$key = ReadKey 0, *TTY;
ReadMode "normal";
printf "\nYou said %s, char number %03d\n",
$key, ord $key;
You want this module: Term::ReadKey.