I have a batch file that runs off a powershell command I created, and I want to make it so you can pass a variable from the PS command to a website url query..
My problem is that the variables I pass from PS are referenced in batch as %1, %2, %n
now I set these variables at the beginning to more meaningful named variables, but in my url eg: www.google.com/myQuery%20has%20spaces would print out my %2 variable from PS instead of a space.
Is there anyway to clear out the %1,%2 variables that are passed? or any work around?
edit: I have tried a simple set %1= to try and set it as a null variable but it didn't work.
You can escape the % in the string with a %% in batch, which solves the issue.
Related
There's a few commands I need to run repetitively in Powershell, with some variable arguments in content and length, but some arguments must always be there. I don't want to forget those arguments, so is there a way to create a function that does this?
I have tried using things like Invoke-Expression, but when I use brackets in a command, Powershell thinks it's a type and tells me my cast is not valid. If I escape the argument list and provide an argument in the form Key=Value, PowerShell parses it into a System.Object[] and the command fails. I figured it might be better not to ask about how to fix that, but how to solve my root problem.
You can consider this a somewhat duplicate of How to always append an ampersand for certain commands? (MacOS / bash) but for PowerShell.
Just write your own version of the command as a Function with mandatory parameters:
Function RunMyCommand {
Param(
[Parameter(Position=0,mandatory=$true)][string]$Argument1,
[Parameter(Position=1,mandatory=$true)][int]$Argument2,
[Parameter(Position=2,mandatory=$true)]$Argument3
)
# Amend this according to the syntax and string manipulation required for your command
ThisIsMyCommand $Argument1 $Argument2 $Argument3
}
RunMyCommand -Argument1 ThisString -Argument2 ThisNumber -Argument3 ThisAnything
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!
I would like to read two parameters that are passed to a batch file. The batch file will be executed from a C++ program using CreateProcess method. The second parameter to the batch file is a folder path, so from the program if I am passing the second parameter such as "E:\test folder\test2" the batch file does not get executed.
But if I instead pass E:\test folder\test2 the batch file gets executed but obviously the second parameter has the value E:\test only.. So what I would like to do is to read the first parameter using %1 and get the rest of the contents into another variable.
Can some one tell me how I can achieve this ? I tried with %* but it gives me both first and second parameters. I would like to remove the first token with space as delimiter so that I have the rest of the contents in the variable. Is there a way to do this ?
For example If I pass test.bat testparameter1 E:\test folder\test folder2\test folder3
I would like to read the value E:\test folder\test folder2\test folder3 into a variable.
If I pass test.bat testparameter1 E:\test\test folderX\test folderY the valueIi want to read in to a variable inside the batch file is E:\test\test folderX\test folderY
Can someone help me with this ? Thanks in advance.
Could you change spaces in the path by another character in your C++ code? For example, if we change spaces by arroba, then you could pass this:
test.bat testparameter1 E:\test#folder\test#folder2\test#folder3
and in the Batch file do the opposite change this way:
set param2=%2
set param2=%param2:#= %
Another possible method is to collect all the parameters from the second one on in the same variable, separating each one by one space:
set param1=%1
shift
set param2=
:nextParam
set param2=%param2% %1
shift
if not "%1" == "" goto nextParam
If your batch file is called with
test.bat testparam1 "E:\test\folder2\test folder 3"
You can read the parameters using %1 and %2
rem Contents of test.bat
#echo %0
#echo %1
#echo %2
The above produces:
C:\Temp>test testparam1 "E:\test\folder2\test folder 3"
test.bat
testparam1
"E:\test\folder2\test folder 3"
C:\Temp>
So you already have the parameters as variables; they're called %1 for the first one, %2 for the second, and so forth.
If the problem is that you're trying to do something using the "E:\test\folder2\test folder 3" path, just make sure you add a trailing backslash before passing it in:
"E:\test\folder2\test folder 3\"
I am writing a release script in powershell and need to invoke a custom executable and pass in a bunch of parameters to the executable. Few of these parameters are assigned values from various conditions and executions in the script. I see that the values are not getting evaluated when the executable is invoked. Some of the variables need to be passed in as a series of arguments inside double quotes as well. Shown below is a smaple call
mysample.exe '-forcerebuild' '-release=$CALC_VERSION' '-projfile=sample.proj' '-buildoptions="/p:AllowDevDependencies=true /p:AssemblyVersion=$CALC_ASSEMBLY_VERSION /flp:LogFile=$env:temp\build.log /p:BaseIntermediateOutputPath=$PROJECT_BASE\build\ /clp:nosummary"'
All the variables that need to be evaluated are highlighted. The exe expects all the options provided to -buildoptions inside quoted -buildoptions="all build options"
The values don't seem to get evaluated. However, if i put build options as a separate variable and print it, the values seem to get evaluated. I am new to powershell and would really appreciate any help with this.
Note that powershell doesn't do variable expansion inside single quoted strings. For example:
$test_variable = "test"
write-host "this is a $test_variable"
write-host 'this is a $test_variable'
will print
this is a test
this is a $test_variable
Also, powershell has problems in passing arguments to exes. There is a util called EchoArgs.exe that comes with Powershell Community Extensions. Use that and pass your argument to that to see if they are passed fine.
I have a variable that is common to most of my app called "emails". I also want to use "emails" as the name of a parameter in one of the scripts. I need to refer to the value of both variables in the same script. Ideally there would be a way to refer using module/namespace or something and perhaps there is but I don't know it. You can see how I hack around this but it is ugly and prone to error. Is there a better way?
# PowerShell v1
# Some variable names are very common.
param ($emails)
# My Hack
# We need to save current value so we have it after we source in variables below.
$emails0=$emails
# Below is going to load a variable called "emails" which will overwrite parm above.
. C:\load_a_bunch_of_global_variables.ps1
It is because as documentation says: (the dot sourcing operator) Runs a script so that the items in the script are part of the calling scope.
In this case I would convert C:\load_a_bunch_of_global_variables.ps1 to a module and pass $emails as parameter or export a function that sets the $script:emails variable in the module. Then the variable will not be in a conflict with the variable in the parent script.
For more information about modules you can use get-help about_modules.
I would avoid using global variables if possible in my scripts.
Why? Because it is a code smell (as programmers say). With one script there is no problem. If two scripts use the same global variable and only read, it is maybe acceptable. But if any of them changes the value, then there might be unpleasant conflicts.
In some cases Get-Variable -scope 1 -name myvariable would help, but I would use it only in closed pieces of code like modules or in short scripts (the same reason as with global variables).
While you can use Get-Variable -scope to get access to variables at arbitrary levels of the call stack, it is easier in this case to grab the top level (to the script) variable using the script: modifier e.g.
$script:emails
rerun and stej both helped me out.
I still want to source in the file using ". file.ps1" but changing "$emails=foo#yahoo.com" in my load_a_bunch_of...ps1 file to "$global:emails=foo#yahoo.com" solved the problem. I can now refer to the variable using global key word when I have a local and a global variable, and when there is only one variable to deal with I can leave out the global keyword.
You can alwways access your global variables from a script using $global:var name inside your script you have local scope and you won't get collisions. If you . source your script you will override the global var.
For Ex if a have a script
$Crap ="test"
$Crap
And you run the flowing commands you get what you want. In line 2 we run the script and the var doesn't get a conflict but if you run the script as in line 4 with a . source you get what you are discovering which due to the way the . operator works
1:PS C:\Users\Adam> $crap = "hi"
2:PS C:\Users\Adam> .\test.ps1
test
3:PS C:\Users\Adam> $crap
hi
4:PS C:\Users\Adam> . .\test.ps1
test
5:PS C:\Users\Adam> $crap
test
6:PS C:\Users\Adam>
if You add the following line to the script run it
$global:crap;
you will get
PS C:\Users\Adam> .\test.ps1
test
hi