'unexpected token' in PowerShell when fully pathing executable - powershell

Just trying to better understand why the second item below does not work. The first item is simple, the second seems clearer, the third seems unintuitive.
# My path includes pscp so this works.
pscp.exe -i $PRIVATE_KEY $file ${PROXY_USER}#${PROXY_HOST}:${PROXY_DIR}
# This does not work. I get unexpected token error. Why? What does that mean?
$PUTTY_PATH\pscp.exe -i $PRIVATE_KEY $file ${PROXY_USER}#${PROXY_HOST}:${PROXY_DIR}
# & is required to solve the problem.
& "$PUTTY_PATH\pscp.exe" -i $PRIVATE_KEY $file ${PROXY_USER}#${PROXY_HOST}:${PROXY_DIR}

That's because this is also considered a parse error:
"foo"\pscp.exe
Whereas this parses correctly as you have found:
"$PUTTY_PATH\pscp.exe"
That resolves to a valid string but as you have already noticed, a string doesn't execute. You have to use the call operator & to invoke the command that is named by the string that follows.

It's taking the \ to be part of the variable name, and complains because it is not legal. If you are using this snippet like i would, by putting it into a .ps1 file in your path, then i would just cd over to $putty_path if you don't want to have pscp.exe in your global PATH env var.

Just guessing, but I have a feeling you might be misusing the curly braces. Are you trying to get the environment variable PROXY_USER instead? Typically the curly brackets are used for starting a new statement block.
$Env:PROXY_USER
Also, you may want to encapsulate that proxy info inside a string to ensure it is treated as a single argument:
"$Env:PROXY_USER#$Env:PROXY_HOST:$Env:PROXY_DIR"

Related

What is the Powershell ${^} variable?

I recently learned that you can use special characters in PowerShell variables, e.g.: ${hello world!}, but then I stumbled across this:
${^}
What does it do? I first thought it referenced the newest created variable, but it does other stuff I haven't really figured out.
The documentation says:
Contains the first token in the last line received by the session.
Examples:
dir c:\windows 🡒 dir
0..9 🡒 0
&{ dir } 🡒 &
It was likely introduced to get the last command used (dir, copy, rm, ...), but in reality that will only work for the most simplest cases, and thus it's not very useful.
The corresponding $$ returns the last token from the last line.
Note: The curly braces {} are only necessary for variable names containing characters which are not allowed in variables, except automatic variables (look here). In this case, you can omit them:
$^
It shows the first word/token in the last executed command.

Is it possible to dot source a string variable in PowerShell?

I know I can dot source a file:
. .\MyFunctions.ps1
But, I would like to dot source the commands in a string variable:
. $myFuctions
I see that this is possible:
.{$x=2}
And $x equals 2 after the script block is sourced.
But... .{$myFunctions} does not work.
I tried $myFunctions | Invoke-Expression, but it doesn't keep the source function in the current scope. The closest I have been able to come up with is to write the variable to a temporary file, dot source the file, and then remove the file.
Inevitably, someone will ask: "What are you trying to do?" So here is my use case:
I want to obfuscate some functions I intend to call from another script. I don't want to obfuscate the master script, just my additional functions. I have a user base that will need to adjust the master script to their network, directory structure and other local factors, but I don't want certain functions modified. I would also like to protect the source code. So, an alternate question would be: What are some good ways to protect PowerShell script code?
I started with the idea that PowerShell will execute a Base64-encoded string, but only when passed on the command line with -EncodedCommand.
I first wanted to dot source an encoded command, but I couldn't figure that out. I then decided that it would be "obfuscated" enough for my purposes if I converted by Base64 file into a decode string and dot sourced the value of the string variable. However, without writing the decoded source to a file, I cannot figure out how to dot source it.
It would satisfy my needs if I could Import-Module -EncodedCommand .\MyEncodedFile.dat
Actually, there is a way to achieve that and you were almost there.
First, as you already stated, the source or dot operator works either by providing a path (as string) or a script block. See also: . (source or dot operator).
So, when trying to dot-source a string variable, PowerShell thinks it is a path. But, thanks to the possibility of dot-sourcing script blocks, you could do the following:
# Make sure everything is properly escaped.
$MyFunctions = "function Test-DotSourcing { Write-Host `"Worked`" }"
. { Invoke-Expression $MyFunctions }
Test-DotSourcing
And you successfully dot-sourced your functions from a string variable!
Explanation:
With Invoke-Expression the string is evaluated and run in the child scope (script block).
Then with . the evaluated expressions are added to the current scope.
See also:
Invoke-Expression
About scopes
While #dwettstein's answer is a viable approach using Invoke-Expression to handle the fact that the function is stored as a string, there are other approaches that seem to achieve the same result below.
One thing I'm not crystal clear on is the scoping itself, Invoke-Expression doesn't create a new scope so there isn't exactly a need to dot source at that point...
#Define your function as a string
PS> $MyUselessFunction = "function Test-WriteSomething { 'It works!' }"
#Invoke-Expression would let you use the function
PS> Invoke-Expression $MyUselessFunction
PS> Test-WriteSomething
It works!
#Dot sourcing works fine if you use a script block
PS> $ScriptBlock = [ScriptBlock]::Create($MyUselessFunction)
PS> . $ScriptBlock
PS> Test-WriteSomething
It works!
#Or just create the function as a script block initially
PS> $MyUselessFunction = {function Test-WriteSomething { 'It works!' }}
PS> . $MyUselessFunction
PS> Test-WriteSomething
It works!
In other words, there are probably a myriad of ways to get something similar to what you want - some of them documented, and some of them divined from the existing documentation. If your functions are defined as strings, then Invoke-Expression might be needed, or you can convert them into script blocks and dot source them.
At this time it is not possible to dot source a string variable.
I stand corrected! . { Invoke-Expression $MyFunctions } definitely works!

calls to cmd from powershell not parsing correctly?

I am trying to run cURL.exe from powershell as part of a larger script to create various user accounts as new individuals are onboarded.
(I cannot use invoke-method or invoke-webrequest because the header parameter wont accept a string)
The issue is that I can run it from a cmd window perfectly but I cannot seem to make the same thing run from powershell:
curl.exe -H "Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}" -d "username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday" "http://URL:8080/v1/users/create"
This runs just fine from cmd but when attempting to run from powershell, I get errors like 'url not found' or 'That path/method is not supported'
Additional format attempts:
encasing the whole thing in single quotes
replacing each /" (forward slash + double quote) with `" (backtick + double quote)
adding all the arguments to a here-string variable
variations of above
I would assume I can write it to a .bat file and then run that from powershell but I'd rather not be forced to do that if possible
The stop parsing parameter is a good choice for this type of thing.
The stop-parsing symbol (--%), introduced in Windows PowerShell 3.0,
directs Windows PowerShell to refrain from interpreting input as
Windows PowerShell commands or expressions.
In your case though you need something a little different as you want variables in the mix that need to be resolved.
I figured there would be a dupe for this. Most of those answer are about converting curl.exe to something like Invoke-WebRequest which is something you should look into. In this case, if you have dynamic content use the call operator like this.
$arguments = "-H ""Authorization: {\""apiKey\"":\""ResetAPIKey,\""username\"":\""admin\""}"" -d ""username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday"" ""http://URL:8080/v1/users/create"""
&"curl.exe" $arguments
Noticed I doubled up the inner quotes on the string. If you look at the variable after it would display properly. Now you should be able to make changes.
Passing complex command line to native application from PowerShell can be tricky. PowerShell can add extra quotes in some cases. Although, rules for this are not that complex, but them are not well (if at all) documented, and also there is some substantial changes in that rules in v2-v4 vs v5. IMHO, using stop parsing operator --%, as #Matt offered, is the best approach, in case you targeting on v3+.
Stop parsing operator does not limit you to use literal strings only. You still can use variables with it. That operator expand environment variables in CMD syntax: %VariableName%. So you can assign required values to some environment variables and use them in command line:
$Env:Env_UserName='someUserName'
$Env:Env_Email='somebody#domain.com'
$Env:Env_FirstName='Joe'
$Env:Env_LastName='Friday'
curl.exe --% -H "Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}" -d "username=%Env_UserName%&email=%Env_Email%&firstName=%Env_FirstName%&lastName=%Env_LastName%" "http://URL:8080/v1/users/create"
Other variant will be to generate full arguments line, you want to pass to native application, and than pass it in single environment variable:
$Arguments=...
$Env:Env_Arguments=$Arguments
curl.exe --% %Env_Arguments%
Matt's answer with the call operator looks like it should work, but if it's not there's a similar alternative I've had good luck with before. Instead of passing your arguments as a string, you can pass them as an array and PowerShell will handle them correctly. In your case, it'd look something like this:
$arguments = #("-H", 'Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}', "-d", '"username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday"', 'http://URL:8080/v1/users/create')
& "curl.exe" $arguments

Passing a variable to a command in a script

I've been searching all over the place and since I'm taking my first steps in PERL this might be one of he dumbest questions but here it goes.
So I'm creating a script to manage my windows and later bind it to keyboard shortcuts, so I I'm trying to run a command and passing some variables:
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$monitors->{1}->{'width'}/2,$monitors->{1}->{'height'}`;
But I get an error saying I'm not passing the right parameters to the command, but if I do this, everything works great:
my $test = $monitors->{1}->{'width'}/2;
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$test,$monitors->{1}->{'height'}`;
So do I really have to do this? assign it first to a variable and then pass it, or there's a more elegant way of doing it?
The backticks operator (or the qx{}) accepts A string which is (possibly) interpolated. So accepts string and not expression like $var/2.
Thats mean than the $variables ($var->{1}->{some} too) are expanded but not the arithmetic expressions.
Therefore your 2 step variant works, but not the first.
If you want evaluate an expression inside the string you can use the next:
my $ans=42;
print "The #{[ $ans/2 ]} is only the half of answer\n";
prints
The 21 is only the half of answer
but it is not very readable, so better and elegant is what you're already doing - calculate the command argument in andvace, and to the qx{} or backticks only pass the calculated $variables.

Tcl set command

Quick question... I'm trying the following:
set changedir "cd $JSDIR/"
eval $changedir
..Where $JSDIR is defined already. Running this script gives the error: "set: Variable name must begin with a letter."
What's the fix?
Thanks.
That's not an error message generated by Tcl, because Tcl's variables most definitely do not need to begin with a letter. OK, it tends to be convenient to keep them alphanumeric because the $var shorthand syntax is more restrictive than the general space of variable names, but the set var form can handle virtually anything.
I'm guessing that script is being evaluated by something that isn't Tcl, perhaps bash?
Try to set changedir cd ${JSDIR/} instead
This message can appear when in fact the variable name is entirely correct, and the real problem is that the variable value needs to be quoted. Try instead of: set changedir "cd $JSDIR//"