PowerShell in bat (multiline code as one line) - powershell

There is a working batch file with PowerShell code in one line:
powershell -Command "& {$cont = wget http://САЙТ.info/; $cont = $cont.Content; $FilePath = 'contentFronHtml.txt' -f $env:SystemDrive; $cont | Out-File -FilePath $FilePath -Append -Width 200;}"
I want to break the PowerShell code to get something like:
SET LONG_COMMAND="$cont = wget http://САЙТ.info/;"
"$cont = $cont.Content;"
"$FilePath = 'contentFronHtml.txt' -f $env:SystemDrive;$cont | Out-File -
FilePath; $FilePath -Append -Width 200;"
powershell -Command "& {LONG_COMMAND}"
How do I connect strings of PowerShell code?

This is how I would expect to do this sort of thing:
PowerShell -Command "first line up to the end of a command;"^
"next line up to the end of a command;"^
"another line up to the end of a command;"^
"last line"
You could also try this idea too:
Set "MyCmnd=first line up to the end of a command;"
Set "MyCmnd=%MyCmnd% next line up to the end of a command;"
Set "MyCmnd=%MyCmnd% another line up to the end of a command;"
Set "MyCmnd=%MyCmnd% last line"
Echo %MyCmnd%

I would save your PowerShell code as a .ps1 file and then run it from the command line:
powershell -File "C:\folder\test.ps1"
Then to break long commands up over multiple lines, follow the answer from: How to enter a multi-line command
This means you're separating your PowerShell code from your batch code, and no longer have inline PowerShell code within a batch file which can get hard to read.

Do something like this:
echo>%temp%\file.ps1 SET LONG_COMMAND="$cont = wget http://САЙТ.info/;"
echo>>%temp%\file.ps1 "$cont = $cont.Content;"
echo>>%temp%\file.ps1 "$FilePath = 'contentFronHtml.txt' -f
echo>>%temp%\file.ps1 $env:SystemDrive;$cont ^| Out-File -
echo>>%temp%\file.ps1 FilePath; $FilePath -Append -Width 200;"
echo>>%temp%\file.ps1 powershell -Command "& {LONG_COMMAND}"
powershell %temp%\file.ps1

Related

powershell quits before conversion done

I am trying to write an automated conversion powershell script to convert las files with potreeconverter.
Here is my script
$ToBeConvertedFolders = 'D:\potree_converter\ToBeConvertedFolders\TestExport1.0'
$ApacheHtdocsFolder = 'C:\xampp\htdocs\projets\'
$InputFormat = 'las'
$PotreeConverter = 'D:\potree_converter\potreeconverter.exe'
Get-ChildItem "$ToBeConvertedFolders\LAS\*.$InputFormat" |
ForEach-Object {
$Filename = $_.BaseName
$InputPath = "`"$ToBeConvertedFolders\$Filename.$InputFormat`""
$OutputPath = "`"$ApacheHtdocsFolder\$Filename`""
Start-Process $PotreeConverter -ArgumentList "$InputPath -o $OutputPath --page-template
c:\potree_converter\resources\page_template\ --generate-page $Filename --title TITILE HERE" -wait
}
Script runs fine and start to process the las file and I have the html output file but it quits without completing the conversion process.
Could you please suggest anything to solve this problem?
Thank you.

Capture cmd error to file in CURRENT directory?

I have a PowerShell script that restarts servers listed in a file.
foreach ($server in $servers) {
try {
cmd /c "shutdown.exe /r /f /m \\$server /t 0 /d p:0:0 /c 'PlannedRestart'"
"$server`tRestarted"
"$server`tRestarted" | Out-File -FilePath .\RestartServers_LOG.txt -Append
} catch {
"$server`t" + cmd /c ">&1"
"$server`t" + cmd /c "dir > .\RestartServers_LOG.txt 2>&1"
}
}
I am trying to catch any errors that may occur from the CMD to output on the PowerShell session as well as to a file in the CURRENT directory, so in PowerShell that would be .\ but I don't know what the current directory specification is for CMD. Unless its the same thing?
Essentially, I am hoping to accomplish something similar to how we can capture the exception message of PowerShell like this:
"$server`t" + $_.Exception.Message.ToString().Split(":")[1].Replace("`n","") |
Out-File -FilePath .\RestartServers_LOG.txt -Append
but CMD doesn't deal with exceptions like PowerShell, and instead STDERR.
Note: I am using the command shutdown.exe because PowerShell doesn't have ability to restart "planned" or "unplanned" unfortunately.
Just do it the PoSh way:
$params = '/r', '/f',
'/t', '0',
'/d', 'p:0:0',
'/c', 'PlannedRestart'
$servers | ForEach-Object {
$output = & shutdown.exe /m "\\${_}" #params 2>&1
if ($LastExitCode -eq 0) {
"{0}`tRestarted" -f $_
} else {
"{0}`tRestart failed:`t{1}" -f $_, $output
}
} | Set-Content '.\RestartServers_LOG.txt'
External commands don't throw exceptions in the first place, so your try..catch wouldn't do anything anyway.

Powershell replace content in file adds redundent carriage return

I have the following script that should be running from a cmd script:
powershell -command "(Get-Content %baseKitPathFile%) | ForEach-Object { $_ -replace 'Latest', '%version%' } | Set-Content %baseKitPathFile%"
The script is working fine and replacing the content of Latest to a version variable however it also adds carriage return after the end of the file
How can I search replace a text file content without the extra carriage return
maybe be trying to use [io.file]:
The most important is that if should be running from cmd script
Set-Content and Out-File both put a linebreak after each line, including the last one. To avoid that you must use an IO.File method:
powershell -Command "$txt = (Get-Content %baseKitPathFile%) -replace 'Latest', '%version%'; [IO.File]::WriteAllText('%baseKitPathFile%', $txt)"
A PowerShell script would be better to handle than the above commandline, though:
[CmdletBinding()]
Param(
[Parameter()][string]$Filename,
[Parameter()][string]$Version
)
$txt = (Get-Content $Filename) -replace 'Latest', $Version
[IO.File]::WriteAllText($Filename, $txt)
Call it like this:
powershell -File "C:\path\to\your.ps1" "%baseKitPathFile%" "%version%"

Executing external command from PowerShell is not accepting a parameter

I am executing the following code attempting to execute the 7z.exe command to unzip files.
$dir contains the user input of the path to the zip file which can contain spaces of course! And $dir\temp2 below is a directory that I previously created.
Get-ChildItem -path $dir -Filter *.zip |
ForEach-Object {
$zip_path = """" + $dir + "\" + $_.name + """"
$output = " -o""$dir\temp2"""
&7z e $zip_path $output
}
When I execute it I get the following from 7z.exe:
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
Processing archive: C:\test dir\test.zip
No files to process
Files: 0
Size: 0
Compressed: 50219965
If I then copy the value from $zip_path and $output to form my own cmd line it works!
For example:
7z e "c:\test dir\test.zip" -o"c:\test output"
Now, I can reproduce the same message "no files to process" I get when I execute within PowerShell by using the following cmd in cli.
7z e "c:\test dir\test.zip" o"c:\test output"
So, it seems that PowerShell is removing the dash char from my -o option. And yes, it needs to be -o"C:\test output" and not -o "c:\test output" with 7z.exe there is no space between the -o parameter and its value.
I am stumped. Am I doing something wrong or should I be doing this a different way?
I can never get Invoke-Expression (alias = &) to work right either, so I learned how to use a process object
$7ZExe = (Get-Command -CommandType Application -Name 7z )
$7ZArgs = #(
('-o"{0}\{1}"' -f $dir, $_.Name),
('"{0}\{1}"' -f $dir, 'temp2')
)
[Diagnostics.ProcessStartInfo]$7Zpsi = New-Object -TypeName:System.Diagnostics.ProcessStartInfo -Property:#{
CreateNoWindow = $false;
UseShellExecute = $false;
Filename = $7ZExe.Path;
Arguments = $7ZArgs;
WindowStyle = 'Hidden';
RedirectStandardOutput = $true
RedirectStandardError = $true
WorkingDirectory = $(Get-Location).Path
}
$proc = [System.Diagnostics.Process]::Start($7zpsi)
$7ZOut = $proc.StandardOutput
$7ZErr = $proc.StandardError
$proc.WaitForExit()
I was able to duplicate the exact issue and tried numerous combinations escaping the -o switch and escaping quotes " and what not.
But as one answer mentioned Sysinternals, and I used Process Monitor to find out the format it was passing to 7z.exe. Things that work on a plain commandline doesn't work inside PowerShell the same way.
For example, if I tried to construct parameters inside PowerShell just like cmdline it would fail. I.e., -o"C:\scripts\so\new folder" doesn't work. But if you include the -o switch inside quotes then PowerShell passes the string "-oC:\scripts\so\new folder" which 7z.exe is happy to accept. So I learned that 7z.exe would accept both the formats such as
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" -o"C:\scripts\so\new folder"
and
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" "-oC:\scripts\so\new folder"
And both examples contain spaces in them.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
[array]$marguments = "e",$_.FullName,"-o$output";
& $pathtoexe $marguments
}
Another approach in PowerShell V3 is to escape the PowerShell parsing feature. You can use the --% command to tell PowerShell to stop parsing any more commands like this.
$zipfile = "C:\scripts\so\newfolder.zip"
$destinationfolder = "C:\scripts\so\New Folder"
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
& $pathtoexe --% e "C:\scripts\so\newfolder.zip" -o"C:\scripts\so\new folder"
Using the --% syntax, you type commands just like you would type them on the command line. I tested this logic, and it extracts files to the destination folder.
To learn more about --%, check PS> help about_parsing.
The issue with this approach is after --% it is not possible to include a variable. The solution to this issue is to just include the --% as another string variable and pass it like this. And this approach is similar to the commandline approach which wasn't working originally.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
$zipfile = $_.FullName;
[string]$formatted = [System.String]::Concat("e ", """$zipfile"""," -o""$output""");
[string]$stopparser = '--%';
& $pathtoexe $stopparser $formatted;
}
Using the excellent Process Explorer from the Windows Sysinternals suite I was able to observe some very interesting behavior. I simplified your command line a little as seen below:
dir -Path $dir -Filter *.zip |
select FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
This was actually invoking the following command line according to Process Explorer:
C:\temp\7za.exe #{FullName="C:\temp\test.zip"} -oC:\temp\test
Telling PowerShell to expand the FullName property forces it out of the hashmap and treats it as a regular string which 7-Zip can deal with:
dir -Path $dir -Filter *.zip |
select -ExpandProperty FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
There may still be other issues like dealing with spaces in file names that I really didn't consider or account for, but I thought it was worth adding a note that PowerShell (v2 in this case) wasn't quite passing the parameters as you might expect.

FTP Download Multiple Files using PowerShell

I’m new to PowerShell and am trying to convert a batch file that downloads multiple files based on names and extension from a directory on an ftp site. While I’ve found several examples that download a file, I’m struggling to find one that shows how to download multiple files. In a batch I can quite simply use the ftp.exe and the mget command with wildcards??
Can someone please point me in the right direction.
Thanks in advance.
John
There are multiple ways to achieve this. One is to use the System.Net.FtpWebRequest as shown in this example:
http://www.systemcentercentral.com/BlogDetails/tabid/143/IndexID/81125/Default.aspx
Or there are /n Software NetCmdlets you can use:
http://www.nsoftware.com/powershell/tutorials/FTP.aspx
In a batch I can quite simply use the ftp.exe and the mget command
with wildcards??
You can do the same in Powershell if you want to.
For a more Powershell way, you can use the FTPWebRequest. See here: http://msdn.microsoft.com/en-us/library/ms229711.aspx. You can build on the example to download multiple files in a loop.
But bottomline is, you do not have to convert something you have in batch to Powershell. You can, if you want, but what you have in batch, especially when calling external programs, should work just as well.
Another resource you might want to check: PowerShell FTP Client Module
http://gallery.technet.microsoft.com/scriptcenter/PowerShell-FTP-Client-db6fe0cb
Oddly enough there are no built in cmdlets to deal with FTP. I'm not sure why the PowerShell team made that decision but it means you'll have to rely on using .NET code, a third party script/module/snap-in or a Win32 program such as FTP.exe as others have already answered with.
Here's is an example of downloading multiple files (binary and text) using .NET code:
$files = "Firefox Setup 9.0.exe", "Firefox Setup 9.0.exe.asc"
$ftpFolder = 'ftp://ftp.mozilla.org/pub/firefox/releases/9.0/win32/en-US'
$outputFolder = (Resolve-Path "~\My Documents").Path
foreach ($file in $files) {
try {
$uri = $ftpFolder + '/' + $file
$request = [Net.WebRequest]::Create($uri)
$request.Method = [Net.WebRequestMethods+Ftp]::DownloadFile
$responseStream = $request.GetResponse().GetResponseStream()
$outFile = Join-Path $outputFolder -ChildPath $file
$fs = New-Object System.IO.FileStream $outFile, "Create"
[byte[]] $buffer = New-Object byte[] 4096
do {
$count = $responseStream.Read($buffer, 0, $buffer.Length)
$fs.Write($buffer, 0, $count)
} while ($count -gt 0)
} catch {
throw "Failed to download file '{0}/{1}'. The error was {2}." -f $ftpFolder, $file, $_
} finally {
if ($fs) { $fs.Flush(); $fs.Close() }
if ($responseStream) { $responseStream.Close() }
}
}
#Jacob. You need ::ListDirectory method to make a list. After, you have to output it in a text file with the out-file command. After that, you import the list with the get-content command. So with a text file, you can make a collection of objects with a foreach loop (don't forget to skip the last line with the '-cne' condition).
You include in this loop your download-ftp function with the parameter of your loop.
Understood ? Not sure if my explanation is good.
So there's an example from one of my script :
$files = Get-FtpList $ftpSource $ftpDirectory $ftpLogin $ftpPassword | Out-File -Encoding UTF8 -FilePath list.txt
$list = Get-Content -Encoding UTF8 -Path list.txt
foreach ($entry in $list -cne "")
{
Get-FtpFile $ftpSource $ftpDirectory $entry $target $ftpLogin $ftpPassword
Start-Sleep -Milliseconds 10
}
Hope it works now for you.
PS:Get-FtpList and Get-FtpFile are custom functions.
This is what i did.As i needed to download a file based on a pattern i dynamically created a command file and then let ftp do the rest
I used basic powershell commands. i did not need to download any additional components
I first Check if the Requisite number of files exist. if they do i invoke the FTP the second time with an Mget.
I run this from a windows 2008 Server connecting to a windows XP remote server
function make_ftp_command_file($p_file_pattern,$mget_flag)
{
# This function dynamically prepares the FTP file
# The file needs to be prepared daily because the pattern changes daily
# Powershell default encoding is Unicode
# Unicode command files are not compatible with FTP so we need to make sure we create an ASCII File
write-output "USER" | out-file -filepath C:\fc.txt -encoding ASCII
write-output "ftpusername" | out-file -filepath C:\fc.txt -encoding ASCII -Append
write-output "password" | out-file -filepath C:\fc.txt -encoding ASCII -Append
write-output "ASCII" | out-file -filepath C:\fc.txt -encoding ASCII -Append
If($mget_flag -eq "Y")
{
write-output "prompt" | out-file -filepath C:\fc.txt -encoding ASCII -Append
write-output "mget $p_file_pattern" | out-file -filepath C:\fc.txt -encoding ASCII -Append
}
else
{
write-output "ls $p_file_pattern" | out-file -filepath C:\fc.txt -encoding ASCII -Append
}
write-output quit | out-file -filepath C:\fc.txt -encoding ASCII -Append
}
########################### Init Section ###############################
$yesterday = (get-date).AddDays(-1)
$yesterday_fmt = date $yesterday -format "yyyyMMdd"
$file_pattern = "BRAE_GE_*" + $yesterday_fmt + "*.csv"
$file_log = $yesterday_fmt + ".log"
echo $file_pattern
echo $file_log
############################## Main Section ############################
# Change location to folder where the files need to be downloaded
cd c:\remotefiles
# Dynamically create the FTP Command to get a list of files from the Remote Servers
echo "Call function that creates a FTP Command "
make_ftp_command_file $file_pattern N
#echo "Connect to remote site via FTP"
# Connect to Remote Server and get file listing
ftp -n -v -s:C:\Clover\scripts\fc.txt 10.129.120.31 > C:\logs\$file_log
$matches=select-string -pattern "BRAE_GE_[A-Z][A-Z]*" C:\logs\$file_log
# Check if the required number of Files available for download
if ($matches.count -eq 36)
{
# Create the ftp command file
# this time the command file has an mget rather than an ls
make_ftp_command_file $file_pattern Y
# Change directory if not done so
cd c:\remotefiles
# Invoke Ftp with newly created command file
ftp -n -v -s:C:\Clover\scripts\fc.txt 10.129.120.31 > C:\logs\$file_log
}
else
{
echo "Full set of Files not available"
}
It's not Powershell specific. But I've tried many other solutions and so far
The http://ncftp.com/ client works the best. It comes with ncftpls.exe for listing remote files and ncftpget.exe for getting files. Use them with Start-Process -Wait
A file list can be constructed in a variable, and used with a regular FTP command....
$FileList="file1_$cycledate.csv
file2_$cycledate.csv
file3_$cycledate.csv
file4_$cycledate.csv"
"open $FTPServer
user $FTPUser $FTPPassword
ascii
cd report
" +
($filelist.split(' ') | %{ "mget $_" }) | ftp -i -n