Powershell - Run external powershell script and capture output - Can't pass parameters - powershell

I have a problem running a powershell script from within another powershell script, passing parameters and capturing the output. I have tried using the ampersand operator, calling it via powershell.exe -Command but nothing seems to work.
What seems to work is using fixed parameter and values stored in a variable like this C:\path\to\script.ps1 -arg1 $value.
This may present a solution if nothing else works, but I would like to run the command similar to this & $pathToScript $params 2>&1 (the 2>&1is for capturing error output as well as standard).
Sometimes the construct prints just the path to the script,
sometimes it says Cannot run file in the middle of pipeline and sometimes it complains about it cannot find the mentioned script file (I sometimes have spaces in my path, but I thought quoting it would suffice: quoting was done like this $path = "`"C:\path\with space\to\script.ps1`"").
This is the simplified function I want to use this in:
Function captureScriptOutput
{
#the function receives the script path and parameters
param($path, $params)
#this works if no params are passed, but I need params!
$output = & $path $params 2>&1 | Out-String
}

I solved the problem with the help of a colleague.
We went a little indirection and included a cd into the respective directory and ran the command afterwards. This works like a charm.
Solution source code:
Function captureScriptOutput
{
param($fileName, $params)
cd "C:\into\path with\space"
$output = & .\$fileName $params 2>&1 | Out-String
}
This works and even captures the error output, I hope some other folks encountering this kind of problem can use this to fix their problems.
Cheerioh and thanks for reply!

Try with invoke-expression but you need test how many quote needed
Invoke-expression "$path $param"

Related

How to execute a string in a variable in powershell

I have the following string
"C:\ProgramData\Package
Cache{6b95042e-f763-4850-9136-d004dd0d0a9b}\AzInfoProtection.exe"
/uninstall
I need to execute the above string as below
First-line
cd C:\ProgramData\Package Cache\{6b95042e-f763-4850-9136-d004dd0d0a9b}
The second line (note there is no exe)
AzInfoProtection /uninstall
Variables are generally executed like below in PowerShell
Invoke-Expression $cmd
But how to split the above string into multiple lines for execution. Then I need to remove the quote and then exe.
It's a bit hard to understand the ask here but I think I follow. Let me know if I'm off base or misunderstanding what you're trying to do.
$commandString = '"C:\ProgramData\Package Cache{6b95042e-f763-4850-9136-d004dd0d0a9b}\AzInfoProtection.exe" /uninstall'
# Get command parent directory
if( $commandString -match '^".*?"' ) {
$runInDir = Split-Path -Parent $Matches[0]
}
# Change directories (use the location stack for easy traversal)
Push-Location $runInDir
# Run program
Invoke-Expression $commandString
# Change back to previous directory
Pop-Location
This works by checking if the string starts with a quote-enclosed string (escaped quotes should not need to be handled within filepaths), and if so gets the first match from the $Matches object. $Matches is an automatic variable which is populated whenever you get a $True result using the [-match operator][1]. With the command path extracted, we use Split-Path to get the parent container relative to the filepath.
Then use Push-Location to change directories. Push-Location works like Set-Location (aliased to cd) except it tracks the directories you leave and enter as a stack. Its sibling cmdlet Pop-Location is used further on to return to the previous location.
Finally, we use Invoke-Expression to run your command. After this completes use Pop-Location to return to the previous directory. Keep the following in mind:
You should take note that the use of Invoke-Expression is often implemented insecurely, and so you should consider heeding the warning on the documentation I've linked to and consider parameterizing your command if your $commandString is actually populated from a generated file, provided by a parameter, or another other outside source.
Note: You mentioned this in your question:
The second line (note there is no exe)
Windows doesn't care if you omit the extension for executable types when executing them. You can run AzInfoProtection.exe with or without the .exe at the end. So unless I'm missing something this detail doesn't have any bearing on how this code works.
To run the string you can pipe it to cmd to run it using:
$commandString | cmd

Powershell Invoke-Expressions pauses

I wrote a Powershell script that uses Steam's command line tool to login and check for updates for a community server I am running. See below:
$steamcmdFolder = 'C:\download\steam'
$steamcmdExec = $steamcmdFolder+"\steamcmd.exe"
$forceinstall = 'force_install_dir'+$steamcmdFolder
$appupdate = 'app_update 258550'
$cmdOutput = "$steamcmdExec +login anonymous"
do {
Write-Host Checking for an update....
Invoke-Expression $cmdOutput
Invoke-expression $forceinstall
Invoke-expression $appupdate
}
while ($Update = 1)
The Invoke-Expression lines are individual command-line statements I want executed in the order I have them. For some reason, the first Invoke-Expression works fine but the others do not -- everything just stops. I can type in the value of $forceinstall on the PowerShell command-line and it works as expected. But why can't I do this using PowerShell? Any suggestions are welcome!
If you convert the other two lines down to what they are, it seems like they are not real commands.
#Invoke-expression $forceinstall
Invoke-Expression "force_install_dirC:\download\steam"
#Invoke-expression $appupdate
Invoke-Expression "app_update 258550"
Looking into the SteamCMD documents, it appears that you might want to change it to be a single line command.
Invoke-Expression "steamcmd +login anonymous +force_install_dir C:\download\steam +app_update 258550 +quit"

Powershell Quotes Use asisstance

Need some expert help from you guru's out there (after 3 days of trying on my own lol). Here the portion of my script that's failing:
$yy=(get-date).Year
Invoke-Command -ComputerName *ipaddress* -Credential $moveitcred {
move-item -path "C:\iCApps\Dev\LetterGenerator\Letters\FTS\EDMS\*.txt" -destination "C:\Moveitft\Dev\Letters\Outbound" -force
Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList "a -y C:\Moveitft\Dev\Letters\archive\Letters$yy_.zip C:\Moveitft\Dev\Letters\Outbound\*.*"
}
The move works fine. AND the zip works fine, except when I want to include the the year varible ($yy) in the argument list. Powershell doesn't want to populate the variable to adjust the file name. Either it bombs out citing a $null, or the job completes with just a blank where the $yy should be.
If i run just $yy from the prompt, it does return the correct year value, so I'm guessing it an improper use of quotes. But after 3 days of googling and trying various combinations on my own, I'm throwing in the towel. Any help/guidance would be so GREATLY appreciated :)
Change Letters$yy_.zip to either
Letters$($yy)_.zip
or
Letters${yy}_.zip.
Both of them work.
See: PowerShell subexpression and PowerShell variable names.
The problem is not about quoting is about variable scope. Inside the scriptblock you are executing, powershell knows nothing about the $yy var. So you need to tell it where to obtain the value. You could do that using the argumentList parameter. I see that you already are doing that but you are doing it wrong (move-item doesn't support that parameter). Look the following examples to grasp the concept, I think with that you will solve your problem:
# this is fine and simple
$yy="Hello scriptblocks!"
Invoke-Command -ScriptBlock { "whatever you want to run that use the an external var. $yy" } -ArgumentList $yy
# this is incorrect
Invoke-Command -ScriptBlock { "whatever you want to run that use the an external var. $yyCarefulWithThisText" } -ArgumentList $yy
# this shows how you could resolve the variable when you want to put text around
Invoke-Command -ScriptBlock { "whatever you want to run that use the an external var. $($yy)CarefulWithThisText" } -ArgumentList $yy
# output1: whatever you want to run that use the an external var. Hello scriptblocks!
# output2: whatever you want to run that use the an external var.
# output3: whatever you want to run that use the an external var. Hello scriptblocks!CarefulWithThisText
Tried the above, thank you! I think this might boil down to powershell version. I'm trying to execute this on a server running powershell v2.0, against another 2012 Server.
I've shortened down the code to just the line in question (so I can "F8" it to test the results) Here it is:
Invoke-Command -ComputerName ipaddress -Credential $moveitcred {
Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -argumentlist "a -y C:\Moveitft\Dev\Letters\archive\Letters${yy}.zip C:\Moveitft\Dev\Letters\Outbound*.*"
}
Both of your suggestions "work", in that the command processes, however its still not reading the variable correctly. Here's what I get as a resultant .zip
The reason I'm thinking its powershell, is becuase if I copy/paste the same command into powershell v4.0 on my own production machine, you can instantly see by the color coding that the "$yy" is being considered a variable because it turns red - and then the code works ok on from my PS4.0 machine.
On the PSv2.0 server i'm trying to get this to run from, that same line for "-argumentlist" appears all maroon/brown - including the $yy portion. So its kind of a hint whether it will work or not for me.

Run Powershell script from inside other Powershell script with dynamic redirection to file

I have been struggling with this problem and researching around but can't get a solution
My problem: I need to run a Powershell script from inside another Powershell script and redirect the output stream to a file. So far so good.
The real issue comes when I need to control the amount of logging through a variable (e.g. write only errors or only error + warning + success output streams).
I can get around it by hard-coding the command as in:
Powershell -File "\path\myscript.ps1" 2>&1> $logFilePathAndName
However, I want to give the user a couple of options for the redirect operator and want to avoid hardcoding each one of them. For that, I was thinking to just code something similar to :
$logStreams = "2>&1>"
Powershell -File "\path\myscript.ps1" $logStreams $logFilePathAndName
The last command does run my script (myscript.ps1 has no input params) but it does not write anything to the file at $logFilePathAndName.
I tried various syntax with Invoke-Command, Invoke-Expression, the call operator and Powershell -Command with no luck.
Looked at this post, had several tries but I can't just get it to work.
For example this runs my script but does not write anything to the output log:
$logStreams = "2>&1>"
$command = '"C:\myscript.ps1" $_logStreams "C:\outputlog.txt"'
iex "& $command"
Is there a way to pass a variable string for the redirect operator OR run a string containing the entire command with a variable interpolated for the output redirector ?
Your last bit there is very close, but I'm not sure why you have the underscore in there, and you need to escape your dollar sign, and close the entire thing in double quotes to cause string extrapolation.
$logStreams = "2>&1>"
$command = "'C:\myscript.ps1' $logStreams 'C:\outputlog.txt'"
iex "& $command"
I just tested that locally and it works fine.

How to call an executable with parameters from powershell script

I am seeking help on how to call cmd with specific parameters from a powershell script. So far what I have written is below, but it gives me an error message saying $_cmd is not recognized.
I am trying to pass the from date and to date to a an exe... The from date as you can see needs to be today - 1 and the to date should be now. The path of the executable is D:\DataService and that's why I am setting the path early in the script.
Write-Host "Get data from service"
$path ="D:\DataService"
Push-Location $path
$Date = Get-Date
$DateFrom = $Date.ToString("yyyy-MM-dd HH:mm:ss")
$DateTo = $Date.AddDays(-1).ToString("yyyy-MM-dd")
$_cmd = "ReportGen.exe -ReportType Data -DateFrom $DateFrom $DateTo"
%$_cmd%
Any suggestions please?
Don't make a command string. Simply use the call operator (&):
Write-Host 'Get data from service'
$path = 'D:\DataService'
Push-Location $path
$Date = Get-Date
$DateFrom = $Date.ToString('yyyy-MM-dd HH:mm:ss')
$DateTo = $Date.AddDays(-1).ToString('yyyy-MM-dd')
& ReportGen.exe -ReportType Data -DateFrom $DateFrom $DateTo
%$_cmd% looks like a mixture of PowerShell and cmd syntax. The %'s have no special significance. PowerShell interprets that as the literal name of a command, which of course is not recognized. To execute the contents of the string, use
Invoke-Expression $_cmd
However, that will only work if ReportGen.exe is in the path, and cmd doesn't even get involved, because you're not calling it anywhere. If for some reason, as you say, you specifically want to execute that command with cmd, you should add cmd /c or cmd /k at the beginning. However, you don't even need to assign to a string, you can just call cmd directly:
cmd /c ReportGen.exe -ReportType Data -DateFrom $DateFrom $DateTo
/c means that cmd will exit after executing the command. /k means the cmd prompt will remain open after the command is executed. You probably want /c so that you'll return to the PowerShell prompt after executing the script.
You want to use Invoke-Expression as per the comments below.
Invoke-Expression $_cmd
Original comment, which is wrong:
Did you try putting an ampersand in front. e.g.:
& $_cmd
Not sure why you are using the % characters.
A couple things. One, stop using Write-Host. Honestly, let this be the final time you ever use it (unless you truly need it - and know why you need it). Instead, use Write-Output - even when you know your script is going to run in the console. Second, you could also consider using the Start-Process cmdlet and its parameter -ArgumentList.
Your $DateFrom and $DateTo seem to be backwards, since you are making $DateTo set to yesterday. So unless you are going from today to yesterday you may want to adjust that. Also, you specify the from date, but just tack the to date on the end, not sure if you need to specify what it is, or if both dates are a part of the same parameter. As for running a command with arguments, use Invoke-Command with arguments comma delimited as such:
Invoke-Command -FilePath "D:\DataService\ReportGen.exe" -ArgumentList '-ReportType Data','-DateFrom $DateFrom','$DateTo'
I'd do it something like this.
$CustomProcess = New-Object System.Diagnostics.ProcessStartInfo
$CustomProcess.FileName = "ReportGen.exe"
$CustomProcess.arguments = "-ReportType Data -DateFrom $DateFrom $DateTo"
[System.Diagnostics.Process]::Start($CustomProcess)