Cannot get exec syntax correct

S

Scott Bass

Active State Perl 5.8
Windoze

I'm trying to write a simple script that will glob all files with a
particular extension, then run a system command against each file. I want
the script to fork off a process for each invocation, and not wait for the
system command to finish.

Here is what I have so far:

#!perl

# set pragmas
use strict;
use warnings;

# define SAS command string(s)
my ($SAS,$args);

# SAS 8
$SAS = q("C:/Program Files/SAS Institute/SAS/V8/sas.exe" -CONFIG "C:/Program
Files/SAS Institute/SAS/V8/SASV8.CFG");

# SAS 9
$SAS = q("C:/Program Files/SAS/SAS 9.1/sas.exe" -CONFIG "C:/Program
Files/SAS/SAS 9.1/nls/en/SASV9.CFG");

# the rest of the options
$args =
q( -rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck -initstmt
"%include '../init.sas';" -sysin );

# include desired modules
use Getopt::Long;
use File::Basename;

# process command line options
my $debug = '0';
GetOptions ('debug!' => \$debug);

# glob files specified on the command line
my @files;
while (@ARGV) {
push @files, glob(shift @ARGV);
}

# process each file
my ($dirname, $basename);
while (@files) {
$_ = shift @files;
$basename = basename($_);
$dirname = dirname($_);

my @cmd = ( "$SAS $args $basename" );

if (! $debug) {
chdir($dirname);
exec { $cmd[0] } @cmd;
}
else {
print "cd $dirname\n";
print @cmd,"\n";
}
}
exit;

It's failing on line 44 (exec command). I've read the doc
file:///C:/Perl/html/lib/Pod/perlfunc.html, exec command, but am still
having problems.

Thanks,
Scott
 
M

Matt Garrish

Scott Bass said:
Active State Perl 5.8
Windoze

I'm trying to write a simple script that will glob all files with a
particular extension, then run a system command against each file. I want
the script to fork off a process for each invocation, and not wait for the
system command to finish.

Here is what I have so far:

#!perl

# set pragmas
use strict;
use warnings;

# define SAS command string(s)
my ($SAS,$args);

# SAS 8
$SAS = q("C:/Program Files/SAS Institute/SAS/V8/sas.exe" -CONFIG
"C:/Program Files/SAS Institute/SAS/V8/SASV8.CFG");

# SAS 9
$SAS = q("C:/Program Files/SAS/SAS 9.1/sas.exe" -CONFIG "C:/Program
Files/SAS/SAS 9.1/nls/en/SASV9.CFG");

Is this your real code? If so, why are you immediately overwriting the $SAS
variable?
# the rest of the options
$args =
q( -rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck -initstmt
"%include '../init.sas';" -sysin );

Uh, $syntaxcheck and %include have not been declared and are not
interpolated when you quote using q//. This may be why your command won't
run...

[snip more code]
It's failing on line 44 (exec command). I've read the doc
file:///C:/Perl/html/lib/Pod/perlfunc.html, exec command, but am still
having problems.

Could you please provide a better description of your problem. You're
expectation is that other people have SAS and can test this code, which is
rarely the case unless you're using built-in system commands...

Matt
 
S

Scott Bass

Hi Matt, good points, see below...

Matt Garrish said:
Is this your real code? If so, why are you immediately overwriting the
$SAS variable?

Yes, if they want to run SAS 9, leave the code as is. If they want to run
SAS 8, comment out the SAS 9 line. Sorry for the confusion.
Uh, $syntaxcheck and %include have not been declared and are not
interpolated when you quote using q//. This may be why your command won't
run...

Sorry again, they are SAS options, and need to be quoted and not
interpolated.
[snip more code]
It's failing on line 44 (exec command). I've read the doc
file:///C:/Perl/html/lib/Pod/perlfunc.html, exec command, but am still
having problems.

Could you please provide a better description of your problem. You're
expectation is that other people have SAS and can test this code, which is
rarely the case unless you're using built-in system commands...

I'm getting this error in the command window:

C:\Temp>'C:/Program' is not recognized as an internal or external command,
operable program or batch file.

I think this is due to the quoting of the SAS command string.

If I change code tidbits to:

# SAS 9
$SAS = q("C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE");

....

my $cmd = "$SAS $basename";

if (! $debug) {
chdir($dirname);
exec ("$cmd");
}

it works...sort of. MS Word invokes, but only on the first file. I need
multiple MS Word sessions to launch for each file.

The doc says:

Using an indirect object with exec or system is also more secure. This usage
(which also works fine with system()) forces interpretation of the arguments
as a multivalued list, even if the list had just one argument. That way
you're safe from the shell expanding wildcards or splitting up words with
whitespace in them.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I've tried a number of permutations from the example in the doc. I either
get the above error or:

Can't exec ""C:/Program Files/SAS/SAS 9.1/sas.exe" -CONFIG "C:/Program
Files/SAS/SAS
9.1/nls/en/SASV9.CFG" -rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck
-initstmt "%include '../init.sas';" -sysin print.sas": Invalid argument at
C:\Documents and Settings\Scott Bass\My Documents\My Perl
Scripts\RunSasPgms.pl line 44.

If I were to execute this in the command window, I would type (exactly,
quotes and all):

"C:/Program Files/SAS/SAS 9.1/sas.exe" -CONFIG "C:/Program Files/SAS/SAS
9.1/nls/en/SASV9.CFG" -rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck
-initstmt "%include '../init.sas';" -sysin test.sas

I have to 1) figure out how to do this with exec, and 2) is exec the proper
approach (fork? threads?) - I need it to invoke say 10 batch SAS sessions
for say 10 files specified on the command line via wildcards. I need the
program to run asynchronously (not pause for each SAS session to finish).

Thanks...
 
A

A. Sinan Unur

I'm trying to write a simple script that will glob all files with a
particular extension, then run a system command against each file. I
want the script to fork off a process for each invocation, and not
wait for the system command to finish.

Since you are running on Windows, why not use start?
Here is what I have so far:

See below for my comments.
#!perl

# set pragmas
use strict;
use warnings;

# define SAS command string(s)
my ($SAS,$args);

# SAS 8
$SAS = q("C:/Program Files/SAS Institute/SAS/V8/sas.exe" -CONFIG
"C:/Program Files/SAS Institute/SAS/V8/SASV8.CFG");

# SAS 9
$SAS = q("C:/Program Files/SAS/SAS 9.1/sas.exe" -CONFIG "C:/Program
Files/SAS/SAS 9.1/nls/en/SASV9.CFG");

# the rest of the options
$args =
q( -rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck
-initstmt "%include '../init.sas';" -sysin );

# include desired modules
use Getopt::Long;
use File::Basename;

# process command line options
my $debug = '0';
GetOptions ('debug!' => \$debug);

# glob files specified on the command line
my @files;
while (@ARGV) {
push @files, glob(shift @ARGV);
}

# process each file
my ($dirname, $basename);
while (@files) {
$_ = shift @files;
$basename = basename($_);
$dirname = dirname($_);

my @cmd = ( "$SAS $args $basename" );

if (! $debug) {
chdir($dirname);
exec { $cmd[0] } @cmd;

You are not really passing a list of arguments to exec, but a single
string. This is really messed up. Do read perldoc -f system.

#!/usr/bin/perl

use strict;
use warnings;

use File::Spec::Functions qw(canonpath catfile);

my %defaults = (
dir => '.',
ext => 'sas',
);

my %args = (
%defaults,
@ARGV,
);

opendir my $dir, canonpath($args{dir})
or die "Cannot open directory: $args{dir}: $!";

FILE:
while(my $file = readdir $dir) {
next FILE unless $file =~ m{ \.$args{ext} \z }x;
run_sas($file);
}

closedir $dir
or die "Cannot close directory: $args{dir}: $!";

my @sas = (
q{C:/Program Files/SAS Institute/SAS/V8/sas.exe},
q{-CONFIG "C:/Program Files/SAS Institute/SAS/V8/SASV8.CFG"},
qw(-rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck
-initstmt "%include '../init.sas';" -sysin),
);

sub run_sas {
my ($sas_file) = @_;

system('start', @sas, $sas_file);
}

__END__

I cannot test this as I do not have any SAS files that I wouldn't mind
risking at this point.

Sinan
 
X

xhoster

Scott Bass said:
Active State Perl 5.8
Windoze

I'm trying to write a simple script that will glob all files with a
particular extension, then run a system command against each file. I
want the script to fork off a process for each invocation, !!!!

and not wait
for the system command to finish.
....

my @cmd = ( "$SAS $args $basename" );

Why is @cmd an array if you are only assigning one thing to it?
if (! $debug) {
chdir($dirname);
exec { $cmd[0] } @cmd;
}
else {
print "cd $dirname\n";
print @cmd,"\n";
}
}
exit;

It's failing on line 44 (exec command).

What does that mean? Does it give an error message? Does you computer
catch on fire?

I've read the doc
file:///C:/Perl/html/lib/Pod/perlfunc.html, exec command, but am still
having problems.

Yes, reading docs doesn't magically fix your code. You have to act on
the things you learned there. For example, you should have learned that
exec *never returns*. So why do you have it inside a loop?

Xho
 
S

Scott Bass

A. Sinan Unur said:
Since you are running on Windows, why not use start?

Doh! Thanks...
Here is what I have so far:
....
# process each file
my ($dirname, $basename);
while (@files) {
$_ = shift @files;
$basename = basename($_);
$dirname = dirname($_);

my @cmd = ( "$SAS $args $basename" );

if (! $debug) {
chdir($dirname);
exec { $cmd[0] } @cmd;

You are not really passing a list of arguments to exec, but a single
string. This is really messed up. Do read perldoc -f system.

I was trying to follow the advice in perldoc -f exec, but obviously I didn't
fully understand it. See the part about indirect objects and the example:

@args = ( "echo surprise" );
#!/usr/bin/perl

use strict;
use warnings;

use File::Spec::Functions qw(canonpath catfile);

my %defaults = (
dir => '.',
ext => 'sas',
);

my %args = (
%defaults,
@ARGV,
);

opendir my $dir, canonpath($args{dir})
or die "Cannot open directory: $args{dir}: $!";

FILE:
while(my $file = readdir $dir) {
next FILE unless $file =~ m{ \.$args{ext} \z }x;
run_sas($file);
}

closedir $dir
or die "Cannot close directory: $args{dir}: $!";

my @SAS = (
q{C:/Program Files/SAS Institute/SAS/V8/sas.exe},
q{-CONFIG "C:/Program Files/SAS Institute/SAS/V8/SASV8.CFG"},
qw(-rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck
-initstmt "%include '../init.sas';" -sysin),
);

sub run_sas {
my ($sas_file) = @_;

system('start', @SAS, $sas_file);
}

__END__

I cannot test this as I do not have any SAS files that I wouldn't mind
risking at this point.

Sinan

Thanks heaps Sinan. It's working now.

I wanted to keep the file globbing code, since our directory structure and
file naming convention is such that typical invocations would be:

runsaspgms sys/?_*.sas pgms/?_*.sas

Many of the .sas programs in a directory would be macros and should not be
submitted directly. The desired programs are named ?_*.sas, and could be in
either of those sub-directories.

I had to add an additional '' to the system command. I'm not sure why this
works; I came across it by accident. Without it, I get an error dialogue:

Cannot find the file '-CONFIG' ... Not sure if it is a system or SAS error
dialogue.

Here is the final code:

#!perl

# set pragmas
use strict;
use warnings;

# include desired modules
use Getopt::Long;
use File::Basename;

# process command line options
my $debug = '0';
GetOptions ('debug!' => \$debug);

# define SAS command string - comment out the version you're not using
my @sas;

# SAS 8
@sas = (
q{C:/Program Files/SAS Institute/SAS/V8/sas.exe },
q{-CONFIG "C:/Program Files/SAS Institute/SAS/V8/SASV8.CFG" },
q{-rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck -initstmt
"%include '../init.sas';" -sysin },
);

# SAS 9
@sas = (
q{C:/Program Files/SAS/SAS 9.1/sas.exe },
q{-CONFIG "C:/Program Files/SAS/SAS 9.1/nls/en/SASV9.CFG" },
q{-rsasuser -icon -nosplash -batch -noterminal -no$syntaxcheck -initstmt
"%include '../init.sas';" -sysin },
);

sub run_sas {
my ($sas_file) = @_;
system('start', '', @sas, $sas_file); # the '' is required, do not
remove
}

# glob files specified on the command line
my @files;
while (@ARGV) {
push @files, glob(shift @ARGV);
}

# process each file
my ($dirname, $basename);
while (@files) {
$_ = shift @files;
$basename = basename($_);
$dirname = dirname($_);

if (! $debug) {
chdir($dirname);
run_sas($basename);
}
else {
print "cd $dirname\n";
print @SAS,$basename,"\n";
}
}

__END__
 
A

A. Sinan Unur

Doh! Thanks...

You are welcome.
I wanted to keep the file globbing code, since our directory structure
and file naming convention is such that typical invocations would be:

runsaspgms sys/?_*.sas pgms/?_*.sas

OK. I have an irrational fear of glob based on the fact that I always
have to look up its semantics.
I had to add an additional '' to the system command. I'm not sure why
this works; I came across it by accident.

Ah! I forgot:

D:\Home\asu1\UseNet\clpmisc> start /?
Starts a separate window to run a specified program or command.

START ["title"] [/Dpath] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/WAIT] [/B] [command/program]
[parameters]
....

So, the first argument to start is the title of the window used to run
the program. You don't need to specify this argument if you are doing
something like:

start picture.jpg

which invokes the associated program.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top