How to pass parameters in PowerShell script which executes in WinSCP command? - powershell

I have a PowerShell script
cd "C:\Program Files (x86)\WinSCP"
. .\WinSCP.exe /console /script="L:\Work\SAS Data\FTP\local2remote.txt" /log=log.txt
It calls WinSCP command file
option batch on
open ftp://user:pass#host -passive=on
lcd "J:\Work\SAS Data\Roman\Lists"
put target.csv
exit
I want to convert "J:\Work\SAS Data\Roman\Lists" to a parameter in the PowerShell script where the parameter can be passed in the txt file. Same goes for the file target.csv. Any help appreciated.

The easiest way (and one that doesn't involve writing a temporary script file) would be to switch to /command for WinSCP:
# You don't need the . operator to run external programs
.\WinSCP.exe /console /log=log.txt /command `
'open ftp://user:pass#host -passive=on' `
'lcd "J:\Work\SAS Data\Roman\Lists"' `
'put target.csv' `
'exit'
Now you have a much easier time incorporating script parameters:
param([string] $Path, [string] $FileName)
& 'C:\Program Files (x86)\WinSCP\WinSCP.exe' /console /log=log.txt /command `
'open ftp://user:pass#host -passive=on' `
"lcd `"$Path`"" `
"put `"$FileName`"" `
'exit'
However, you can of course, still write a command file and pass that:
$script = Join-Path $Env:TEMP winscp-commands.txt
"open ftp://user:pass#host -passive=on
lcd ""$Path""
put ""$FileName""
exit" | Out-File -Encoding Default $script
& 'C:\Program Files (x86)\WinSCP\WinSCP.exe' /console /log=log.txt /script=$script
Remove-Item $script

Related

WinSCP: How do you add a PowerShell variable to a WinSCP PowerShell script?

I'm trying to build log path and lcd variables in my WinSCP script and I don't know the proper syntax to make it work.
How can I put the variables into the script correctly? This is an example of what I'm trying to do.
$logPath = "c:\LogPath"
$lcdPath = "c:\lcdPath"
& "C:\Program Files (x86)\WinSCP\WinSCP.com" `
/log = $logPath /ini=nul `
/command `
"open ftpSite -hostkey=`"`"hostKey`"`" -rawsettings FSProtocol=2" `
"cd ftpFilePath" `
$lcdPath `
"get * -filemask=*>=1D" `
"exit"
Your syntax for using variables is correct. It's your WinSCP command-line syntax that is wrong.
There should be no spaces around switch values. And the values should better be quoted, in case they contain spaces:
/log="$logPath"
The /log takes path to a file, not path to a directory:
$logPath = "c:\LogPath\WinSCP.log"
You are missing the actual lcd command:
"lcd $lcdPath" `
The only PowerShell issue I see in your code is that the backtick that is meant to escape a line end, has to be the last character on the line. While you have spaces after the backtick in the WinSCP.com line:
& "C:\Program Files (x86)\WinSCP\WinSCP.com" `#no spaces here

Process files dragged to BAT file in PowerShell

I trying to create a script that will convert Markdown files that are dropped on a processing script.
To accomplish this, I created a (process.bat) that would pass the names of the dropped files to a PowerShell script:
powershell.exe -NoProfile -File "./process.ps1" -Document %*
#pause
The PowerShell file (process.ps1) would process each file individually:
[parameter(Mandatory=$true)]
[String[]]$Document
Write-Host $args[1]
$Document | ForEach-Object {
Write-Host "Document: $_"
# convert Markdown to Html
pandoc -o ($_ -Replace '.md', '.html') -f markdown -t html $_
}
When I drop two files on the batch file:
C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown>powershell.exe -NoProfile -File "./process.ps1" -Document "C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\FOO.md"
"C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\BAR.md"
C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\BAR.md
Document:
Press any key to continue . . .
The documents are not being processed.
What's the recommended approach to passing the batch file's file list %* to PowerShell?
When powershell.exe, the PowerShell CLI, is called from the outside with the -File parameter, it doesn't support arrays - only individual arguments.[1]
(Additionally, you neglected to wrap your parameter definition in a param(...) block, which effectively caused it be ignored.)
The simplest solution is to define your parameter with the ValueFromRemainingArguments option so that it automatically collects all positional arguments in the parameter variable:
param(
[Parameter(Mandatory, ValueFromRemainingArguments)]
[String[]] $Document
)
Then invoke your script via the PowerShell CLI without -Document:
powershell.exe -NoProfile -File "./process.ps1" %*
As an alternative to using a helper batch file, you can:
define a shortcut file (*.lnk) that explicitly invokes your PowerShell script as powershell.exe -File \path\to\your\script.ps1 (without additional arguments)
and then use that as the drop target.
Note: The reason that you cannot use a PowerShell script (*.ps1) directly as a drop target is that PowerShell script files aren't directly executable - instead, opening (double-clicking) a *.ps1 file opens it for editing.
You'd then need to add pause (or something similar, such as Read-Host -Prompt 'Press Enter to exit.') to your PowerShell script, to prevent it from closing the window instantly on finishing.
Alternatively, leave the script as-is and use -NoExit (placed before -File) to keep the PowerShell session open.
[1] The same applies to the PowerShell Core CLI, pwsh.

Run a powershell command with arguments from the Run line

I am still learning PowerShell and the Windows run line seems to make it even harder
Question: How can I do this directly from the run line (if possible an admin powershell) but I can deal with clicking yes after the the download... it just slows down the process
wget 'https://MYSERVER/MYFILE.MSI' -O PROGRAM.msi; start PROGRAM.msi /qn
This works great when powershell is already open as admin, also works when powershell is open as normal user, but I have to wait for the program to be downloaded to click yes instead of clicking yes to the admin powershell and let the rest autoinstall.
I tried
Powershell -Command 'wget...
but not working
Point of note: wget in PoSH is an alias
Get-Alias -Name wget
CommandType Name
Alias wget -> Invoke-WebRequest
... and with the way you are doing this, you are using wget.exe, not the above.
So, you can use wget.exe, but you have to specify the full UNC to wget.exe if it is not in your system path. That .exe is a must.
Or you need to remove the aliases
Remove-Item Alias:WGet
To download files from the web, look at the Invoke-WebRequest examples
Get-Help -Name 'Invoke-WebRequest' -Examples
Or write your own function using .Net, someting like the below and put it in your PoSH user profile
Function New-ToolDownloadInstall ($url)
{
# Set the webclient
$webclient = New-Object System.Net.WebClient
# Extract the filename from the URL and Download
$filename = [System.IO.Path]::GetFileName($url)
$file = "$env:USERPROFILE\Downloads\$filename"
$webclient.DownloadFile($url,$file)
# Remove the web ADS
Unblock-File -Path $file
# Install the file
Start-Process $file -NoNewWindow -wait
}
# Use the function
New-ToolDownloadInstall -url 'https://download.microsoft.com/download/5/0/1/5017D39B-8E29-48C8-91A8-8D0E4968E6D4/en/msoidcli_64.msi'
Here is another example
Download files from websites programatically via powershell
https://gallery.technet.microsoft.com/scriptcenter/files-from-websites-4a181ff3
Also, some DOS level command have to be called this way, if you are in the PowerSHell_ISE.exe.
Start-Process "$PSHOME\powershell.exe" -ArgumentList "-NoExit","-Command &{ wget.exe 'https://MYSERVER/MYFILE.MSI' -O PROGRAM.msi; start PROGRAM.msi /qn }"
See more details here:
https://blogs.technet.microsoft.com/josebda/2012/03/03/using-windows-powershell-to-run-old-command-line-tools-and-their-weirdest-parameters

Redirecting standard input\output in Windows PowerShell

What is the required syntax to redirect standard input/output on Windows PowerShell?
On Unix, we use:
$./program <input.txt >output.txt
How do I execute the same task in PowerShell?
You can't hook a file directly to stdin, but you can still access stdin.
Get-Content input.txt | ./program > output.txt
If there is someone looking for 'Get-Content' alternative for large files (as me) you can use CMD in PowerShell:
cmd.exe /c ".\program < .\input.txt"
Or you can use this PowerShell command:
Start-Process .\program.exe -RedirectStandardInput .\input.txt -NoNewWindow -Wait
It will run the program synchronously in same window. But I was not able to find out how to write result from this command to a variable when I run it in PowerShell script because it always writes data to the console.
EDIT:
To get output from Start-Process you can use option
-RedirectStandardOutput
for redirecting output to file and then read it from file:
Start-Process ".\program.exe" -RedirectStandardInput ".\input.txt" -RedirectStandardOutput ".\temp.txt" -NoNewWindow -Wait
$Result = Get-Content ".\temp.txt"
For output redirection you can use:
command > filename Redirect command output to a file (overwrite)
command >> filename APPEND into a file
command 2> filename Redirect Errors
Input redirection works in a different way. For example see this Cmdlet http://technet.microsoft.com/en-us/library/ee176843.aspx
Or you can do:
something like:
$proc = Start-Process "my.exe" "exe commandline arguments" -PassThru -wait -NoNewWindow -RedirectStandardError "path to error file" -redirectstandardinput "path to a file from where input comes"
if you want to know if process errored out, add following code:
$exitCode = $proc.get_ExitCode()
if ($exitCode){
$errItem = Get-Item "path to error file"
if ($errItem.length -gt 0){
$errors = Get-Content "path to error file" | Out-String
}
}
I find that this way I do have a better handle on execution of your scripts, when you need to handle external program/process. Otherwise I have encountered situations where script would hang out on some of external process errors.
You can also do this to have standard error and standard out go to the same place (note that in cmd, 2>&1 must be last):
get-childitem foo 2>&1 >log
Note that ">" is the same as "| out-file", and by default the encoding is unicode or utf 16. Also be careful with ">>", because it can mix ascii and unicode in the same text file. "| add-content" probably works better than ">>". "| set-content" might be preferable to ">".
There's 6 streams now. More info: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-5.1
I think all you can do is save to a text file and then read it into a variable after.

how to Execute a cmd file in my ps script?

I have a cmd file which calls an msi and passes paramters. I am calling this deploy.cmd file from a powershell script. How can i achive this?
I am may be missing something here.
This is what my cmd looks like,
Msiexec /i ABCInstaller.msi ^
DB.SERVER=ABC\QA ^
APPLICATION.ENV.TYPE=Qa ^
SVCIDENTITY=SVC-QA#ABC.com ^
SVCPASSWORD=xxx ^
LOCAL.EMAILING="true" ^
EMAIL.GMAT="tarun.arora#abc.com" ^
EMAIL.GMATR="tarun.arora#abc.com" ^
EMAIL.SUCCESSFUL.VALIDATION.SUBJECT="[QA] Successful validation of ABC Message" ^
/lv "ABC_Installer_QA_Log.txt"
This is what my powershell script looks like,
# Assigning Build Number and Drop Location for the MSI in scope
$buildNumber = $TfsDeployerBuildData.BuildNumber
$dropLocation = $TfsDeployerBuildData.DropLocation
# Assign values
if($buildNumber -eq $null)
{
$buildNumber = $args[0]
$dropLocation = $args[1]
}
# Move old uninstall folder to Archive folder
Move-Item "D:\deploy\ABC_Uninstalled\*" "D:\deploy\ABC_Archive" -force
# Move old build folder to uninstalled folder
Move-Item "D:\deploy\ABC_Installed\*" "D:\deploy\ABC_Uninstalled" -force
# Logging
Add-Content -Path "C:\Log\TfsDeployer_Log.txt" -Value $dropLocation
Add-Content -Path "C:\Log\TfsDeployer_Log.txt" -Value $buildNumber
# Copy the msi from drop location to local physical drive
Copy-Item $dropLocation "D:\deploy\ABC_Installed" -recurse
Add-Content -Path "C:\Log\TfsDeployer_Log.txt" -Value "Copied the Msi to D:\deploy\Installed"
# Start execution
& "D:\deploy\ABC_Installed\$buildNumber\en-us\ETRM_QA.cmd"
However when the ps is executed, It prints out what is inside the cmd file rather than executing it, so the output of the execution is
Output: C:\WINDOWS\system32>Msiexec /i ABCInstaller.msi ^
DB.SERVER=ABC\QA ^
APPLICATION.ENV.TYPE=Qa ^
SVCIDENTITY=SVC-QA#ABC.com ^
SVCPASSWORD=xxx ^
LOCAL.EMAILING="true" ^
EMAIL.GMAT="tarun.arora#abc.com" ^
EMAIL.GMATR="tarun.arora#abc.com" ^
EMAIL.SUCCESSFUL.VALIDATION.SUBJECT="[QA] Successful validation of ABC Message" ^
/lv "ABC_Installer_QA_Log.txt" /passive T
The cmd file is not executed :-(
Try:
Invoke-Expression "D:\deploy\ABC_Installed\$buildNumber\en-us\ETRM_QA.cmd"
MsiExec likely is executing, you are just not seeing it because it launches as a background process that immediately returns control to cmd. For example, if I create a cmd script that looks like this:
"C:\Program Files\Microsoft Office\Office11\WINWORD.EXE"
And invoke it like this:
&launchWord.cmd
All I see on the powershell console is the contents of the cmd script, but word opens in another window.
Are you sure msiexec isn't just launching and failing, rather than failing to launch?