How to place variable in Powershell try/catch block - string

I have a variable in my script for my path:
$path = "C:\project\"
Then in a function I have:
function DoWork {
try
{
C:\project\myapp.exe /silent
}
catch
{
}
}
I want to replace the string literal in the try block with the variable. I have tried putting ${path} there, and ($path + "myapp.exe /silent") along with other combinations but I seem to get errors still.
What is the correct way to replace the literal path with the path variable?

Construct your path as a string, then supply the string as an argument to the call operator (&):
try
{
$appPath = Join-Path $path myapp.exe
& $appPath
}
catch
{
}
Subsequent string arguments to & will be treated as arguments to the command represented by the first argument:
& $appPath '/silent'

To pass a dynamic list of arguments to an external function call (based on your comments), use splatting:
$Params = #('/silent','/quiet')
$exe = "$Path\myapp.exe"
& $exe #Params

You can also try this:
function DoWork {
$path = "C:\project\"
try
{
"$path\myapp.exe"
}
catch
{
}
}

Related

Groovy closure return of value to variable

Very basic question but I cannot find an answer:
I have the below code in a file g.groovy, and it functions in printing output:
#! /usr/env/groovy
def matchFiles = { match ->
new File(".").eachFile() {
if (it.name =~ match) {
println it
}
}
}
matchFiles('.groovy') prints ./g.groovy to screen.
But I want to capture the output of the closure in a variable and use it elsewhere, e.g.
def fileMatches = matchFiles('.groovy')
but cannot figure this out.
Tried changing println it to return it and then running
def fileMatches = matchFiles('.groovy')
fileMatches.println { it }
but this prints something like g$_run_closure2#4b168fa9
Any help is much appreciated, sorry for any incorrect nomenclature, very new to Groovy
according to the name matchFiles I assume you want to return all matched files
so, you have to define an array result variable where you are going to store each matched file
and then return this result variable after eachFile{...} closure
def matchFiles = { match ->
def result=[]
new File(".").eachFile {
if (it.name =~ match) {
result.add(it)
}
}
return result
}
println matchFiles(/.*/)

Distinguish one sub invocation from another

In the following fragment, how can I distinguish the second invocation instance of my sub foo from the first?
while ($whatever) {
foo(); foo(); # foo() and foo() have the same caller package, file, and line
}
Something like a super-caller() that returned file, line and column would do the trick. I'd prefer not to use source filters.
Background, or, isn't this a bit of an XY Problem?
I have a convenience module, Local::Thread::Once, that exposes functionality like pthread_once/std::call_once in an OO-ish way and also as a subroutine attribute. These are easy enough, since there is a natural and unambiguous "once_control" or "once_flag" in either case.
However, there is additionally a procedural interface — once { ... } — that currently serializes based on the $filename and $line returned by caller. Something like this:
sub once(&) {
my $user_routine = shift;
my (undef, $file, $line) = caller;
my $once_control = get_a_shared_flag_just_for_this_invocation($file, $line);
lock($once_control);
if (! $once_control) { $once_control++; $user_routine->(); }
return;
}
That's not precisely how it works — the real one is more efficient — but the point, again, is that invocation is keyed off of the file and line of the caller. This works, except that it cannot distinguish two invocations on the same line.
while ($whatever) {
once { foo(); }
once { bar(); } # OK, foo() and bar() each called only once
once { baz(); }; once { buz(); }; # :( buz() not called, not even once
}
Note that the address of $user_routine cannot be used as an additional discriminant, since subs are copied from one ithread to another.
I can live with this problem as a documented limitation for a very contrived use case, but I'd prefer to fix it somehow.
Devel::Callsite was written precisely for this purpose.
I had to read this a couple of times before I understood what you are talking about. How about a "super caller" function like:
my #last_caller = ("","","",0);
sub super_caller {
my ($pkg,$file,$line) = caller(1 + shift);
if ($pkg eq $last_caller[0] &&
$file eq $last_caller[1] &&
$line eq $last_caller[2]) {
$last_caller[3]++;
} else {
#last_caller = ($pkg,$file,$line,1);
}
return #last_caller;
}
It's like caller but the 4th element is a count of how many times we've seen this exact package, file, and line in a row.
The optree is still so much black magic to me, but here are my observations:
in walking the optree of a code reference, you encounter one B::COP structure
The B::COP structure has file, line, and cop_seq properties (among others)
The cop_seq property is different for different subroutine definitions
Ass-u-me-ing these are true and not a horribly incomplete model of what is happening, you can use file, line, and cop_seq as a key, or maybe even just cop_seq. Here's a proof of concept:
use B;
sub once (&) {
my $code = shift;
my $key = get_cop_seq($code);
print "once called with code '$key'\n";
}
my $optreedata;
sub get_cop_seq {
my $code = shift;
$optreedata = "";
B::walkoptree( B::svref_2object($code)->ROOT, "find_cop_seq" );
return $optreedata;
}
sub B::OP::find_cop_seq {
my $op = shift;
if (ref $op eq 'B::COP') {
$optreedata .= sprintf "%s:%d:%d", $op->file, $op->line, $op->cop_seq;
}
}
sub foo { 42 }
sub bar { 19 };
once { foo }; # this is line 26
once { bar };
once { foo }; once { bar };
once { bar } for 1..5; # line 29
And here's the output (your results may vary):
once called with code 'super-caller2.pl:26:205'
once called with code 'super-caller2.pl:27:206'
once called with code 'super-caller2.pl:28:207' <--- two calls for line 28
once called with code 'super-caller2.pl:28:208' |- with different cop_seq
once called with code 'super-caller2.pl:29:209'
once called with code 'super-caller2.pl:29:209'
once called with code 'super-caller2.pl:29:209' <--- but 5 calls for line 29
once called with code 'super-caller2.pl:29:209' with the same cop_seq
once called with code 'super-caller2.pl:29:209'

String read in from file not responding to string manipulation

I have a Perl subroutine that creates a file, like so:
sub createFile {
if (open (OUTFILEHANDLE, ">$fileName")) {
print OUTFILEHANDLE "$desiredVariable\n";
}
close(OUTFILEHANDLE);
}
where $fileName and $desiredVariable have been previously defined. I call that, and then call the following subroutine, which reads from the file, takes the first (only) line, and saves it into the variable $desiredVariable:
sub getInfoFromFile {
if (existFile($fileName)) {
if (open (READFILEHANDLE, "<$fileName")) {
my #entire_file=<READFILEHANDLE>; # Slurp
$desiredVariable = $entire_file[0];
chop $desiredVariable;
close(READFILEHANDLE);
}
}
}
If I leave out the "chop" line, $desiredVariable is what I want, but with a trailing space newline. If I include the "chop" line, $desiredVariable is an empty string. For some reason, "chop" is killing the whole string. I've tried it with $desiredVariable =~ s/\s*$//; and several other string manipulation tricks.
What am I doing wrong?
The code you included does not reproduce the problem. I'm guessing it was lost in translation somehow while you were anonymizing it. I ran the script as follows, the only adjustment I made was -f instead of existsFile().
#!/usr/bin/perl
sub createFile {
if (open (OUTFILEHANDLE, ">$fileName")) {
print OUTFILEHANDLE "$desiredVariable\n";
}
close(OUTFILEHANDLE);
}
sub getInfoFromFile {
if (-f $fileName) {
if (open (READFILEHANDLE, "<$fileName")) {
my #entire_file=<READFILEHANDLE>; # Slurp
$desiredVariable = $entire_file[0];
chop $desiredVariable;
close(READFILEHANDLE);
}
}
}
$fileName = "test.txt";
$desiredVariable = "Hello World!";
createFile();
$desiredVariable = "";
getInfoFromFile();
print "Got '$desiredVariable'\n"; # Got 'Hello World!'

perl threads create - how to correctly specify class instance method?

Having problems with threads. Keep getting error when creating a thread using a class instance method as the subroutine. The method and params variables are set based on other stuff, so I have to call the class instance method this way. Without the threads, it works just fine. Can't figure out the correct way to specify it for threads create:
my $instance = someclass->new();
my $method = 'get';
my $params = { 'abc' => 123 };
my $thread = threads->create($instance->$method,$params);
This gives me the error "Not a CODE reference". I think this may be actually calling the method, and using the return as the argument. Okay, tried this:
my $thread = threads->create(\&{$instance->$method},$params);
This gives me the error "Not a subroutine reference". I would appreciate any help on this.
my $thread = threads->create(sub { $instance->$method(#_) }, $params);
Or, you could just pass the instance and the method to the first argument as well:
package SomeClass;
sub new {
my $class = shift;
bless { args => [ #_ ] };
}
sub get {
my $self = shift;
my $args = shift;
return join(" ", #{ $self->{args} }, $args->{abc});
}
package main;
use 5.012;
use threads;
my $x = SomeClass->new("An instance");
threads->create(sub { say $x->get(#_) }, {'abc' => 123 })->join;
threads->create(
sub {
my $instance = shift;
my $method = shift;
say $instance->$method(#_);
}, $x, 'get', { 'abc' => 123 }
)->join;
In fact, I would prefer the latter, to avoid closing on $instance.
Calling a method without parens is the same thing as calling the method without arguments:
$foo->bar eq $foo->bar()
To create a coderef, you can either specify a lambda that wraps the method call, e.g.
threads->create(sub{ $instance->get($params) })
(see Sinan Ünürs answer), or you can use the universal can function.
The can method resolves a method in the same way a method would be resolved if it were called, and returns the coderef for that method if it was found, or returns undef. This makes it usable as a boolean test.
Do note that methods are just subroutines with the first argument being the invocant (the object):
my $code = $instance->can($method) or die "Can't resolve $method";
threads->create($code, $instance, $params);
However, can may fail for poorly written classes that make use of AUTOLOAD.

How do I do a string replacement in a PowerShell function?

How do I convert function input parameters to the right type?
I want to return a string that has part of the URL passed into it removed.
This works, but it uses a hard-coded string:
function CleanUrl($input)
{
$x = "http://google.com".Replace("http://", "")
return $x
}
$SiteName = CleanUrl($HostHeader)
echo $SiteName
This fails:
function CleanUrl($input)
{
$x = $input.Replace("http://", "")
return $x
}
Method invocation failed because [System.Array+SZArrayEnumerator] doesn't contain a method named 'Replace'.
At M:\PowerShell\test.ps1:13 char:21
+ $x = $input.Replace( <<<< "http://", "")
Steve's answer works. The problem with your attempt to reproduce ESV's script is that you're using $input, which is a reserved variable (it automatically collects multiple piped input into a single variable).
You should, however, use .Replace() unless you need the extra feature(s) of -replace (it handles regular expressions, etc).
function CleanUrl([string]$url)
{
$url.Replace("http://","")
}
That will work, but so would:
function CleanUrl([string]$url)
{
$url -replace "http://",""
}
Also, when you invoke a PowerShell function, don't use parenthesis:
$HostHeader = "http://google.com"
$SiteName = CleanUrl $HostHeader
Write-Host $SiteName
Hope that helps. By the way, to demonstrate $input:
function CleanUrls
{
$input -replace "http://",""
}
# Notice these are arrays ...
$HostHeaders = #("http://google.com","http://stackoverflow.com")
$SiteNames = $HostHeader | CleanUrls
Write-Output $SiteNames
The concept here is correct.
The problem is with the variable name you have chosen. $input is a reserved variable used by PowerShell to represent an array of pipeline input. If you change your variable name, you should not have any problem.
PowerShell does have a replace operator, so you could make your function into
function CleanUrl($url)
{
return $url -replace 'http://'
}
function CleanUrl([string] $url)
{
return $url.Replace("http://", "")
}
This worked for me:
function CleanUrl($input)
{
return $input.Replace("http://", "")
}

Resources