My Powershell script works fine this way (it uses FTP to send IHM_OTP_CRT_20190812_0701.txt to the server and save it as newfile.txt)
$File = "Z:\Export\IHM_OTP_CRT_20190812_0701.txt"
$ftp = "ftp://ftpuse:mypass#e2b.kpsci.com/inbound/newfile.txt"
"ftp url: $ftp"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
"Uploading $File..."
$webclient.UploadFile($uri, $File)
But it does not work if I change the second line
$ftp = "ftp://ftpuse:mypass#e2b.kpsci.com/inbound/newfile.txt"
to the following
$File = Get-ChildItem -Recurse | Where-Object { $_.Name -match 'IHM_OTP_CRT_.' } | sort LastWriteTime | select -last 1
$NewFileName = $File.Name
$ftp = "ftp://ftpuse:mypass#e2b.kpsci.com/inbound/" + $NewFileName
and this is driving me crazy.
I've tried various concatenation methods...I used the $var1``$var2 concept saved into $NewFileName to avoid the + sign, I've used parenthesis around the argument like:
$ftp = ("ftp://ftpuse:mypass#e2b.kpsci.com/inbound/" + $NewFileName)
And the more frustrating part is that when I use #echo, the concatenated string looks perfect. Also, this works fine:
$ftp = "ftp://ftpuse:mypass#e2b.kpsci.com/inbound/" + "IHM_OTP_CRT_20190812_0701.txt"
So, it's only concatenating with a separate object (even if it's a string) that doesn't work. I can concatenate to "blah" but not to a variable that equates to "blah". I've spent hours on this and I don't imagine it should be this difficult.
The error I am receiving is:
"Exception calling "UploadFile" with 2 argument(s): "The requested URI is invalid for this FTP command...at :17 char: 22..."
The error makes sense to me - it think's that my "concatenated" object contains a separate argument into the Upload File method, but I can't understand how to make it understand that I'm intending to pass a single string.
The issue is that the Get-ChildItem object .ToString() will only return the file name, not the full path. Therefore in the Webclient Upload you have to specify the .FullName property:
$webclient.UploadFile($uri, $File.FullName)
Full code:
$File = Get-ChildItem -Recurse | Where-Object { $_.Name -match 'IHM_OTP_CRT_.' } | sort LastWriteTime | select -last 1
$NewFileName = $File.Name
$ftp = "ftp://ftpuse:mypass#e2b.kpsci.com/inbound/" + $NewFileName
"ftp url: $ftp"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
"Uploading $File..."
$webclient.UploadFile($uri, $File.FullName)
Related
Code below to download a file from website:
https://www.mcafee.com/enterprise/en-us/downloads/security-updates.html
File being downloaded
However, the file name is changing every day. For example: 'mediumepo4981dat.zip' today and 'mediumepo4980dat.zip' yesterday.
How can I create a script which I can run daily which works dynamically?
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$uri = "https://download.nai.com/products/datfiles/med/mediumepo4981dat.zip"
$filename = "C:\DownloadTest\mediumepo4981dat.zip"
$wc = New-Object System.Net.WebClient
$wc.UseDefaultCredentials = $true
$wc.DownloadFile($uri, $filename)
Please let me know if you need any further clarification.
Note: Looking for a solution without Invoke-WebRequest as that does not work in my current environment.
Seems like we can assume that the last file listed is always the newest, can't confirm if this will always be true it may change at some point. For the time being, this is how you can get the last file:
$uri = [uri] "https://download.nai.com/products/datfiles/med"
$wr = Invoke-WebRequest $uri
$file = $wr.ParsedHtml.getElementsByTagName('a') |
Select-Object -Last 1 -ExpandProperty TextContent
$toDownload = [uri]::new($uri, $file)
Now you can combine this with the rest of your script:
$filename = Join-Path 'C:\DownloadTest\' -ChildPath $file
$wc = New-Object System.Net.WebClient
$wc.UseDefaultCredentials = $true
$wc.DownloadFile($toDownload.AbsoluteUri, $filename)
Note that this will work only in Windows PowerShell.
$uri = [uri] "https://download.nai.com/products/datfiles/med"
$wr = Invoke-WebRequest $uri
$file = $wr.ParsedHtml.getElementsByTagName('a') |
Select-Object -Last 1 -ExpandProperty TextContent
$toDownload = [uri]::new($uri, $file)
is missing trailing '/' in $uri.
It should be
$uri = [uri] "https://download.nai.com/products/datfiles/med/"
I'm trying to read excel files into Powershell, open, password protect them and write them back. I can do it individually but within a loop the script fails:
#working individually
$f = ("C:my\path\Out Files\1234dv.xlsx")
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($f)
$a = $wb.SaveAs("C:my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
#not working in loop, error after
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
foreach ($f in Get-ChildItem "C:\my\path\Out Files"){
$ff = $f
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($ff)
$a = $wb.SaveAs("C:\my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
}
Sorry, we couldn't find 1234dv.xlsx. Is it possible it was moved,
renamed or deleted? At line:16 char:5
+ $wb = $xl.Workbooks.Open($ff)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException COM object that has been
separated from its underlying RCW cannot be used. At line:17 char:5
+ $a = $wb.SaveAs("C:\my\path ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidComObjectException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.InvalidComObjectException
That error repeats for all four test files I'm working with.
I'm not really familiar with Powershell so I relied on MS docs, and I couldn't password protect the files in python so thought this would be easier. I know this doesn't address the password yet either but trying to get the loop to work first. Any help would be greatly appreciated. Thank you.
You should use
$wb = $xl.Workbooks.Open($ff.FullName)
To give Excel the full file path. Otherwise, $ff is a FileInfo object where a string (path) is required
Slightly off topic for your question , but not for your intent :
From a security perspective using .xls passwords is not security, it is merely an annoyance.
If you need security, then i suggest you use something like Azure Information protection that allows you to encrypt , and share the file securely only with those that need access.
You still need to create your xls or .xlsx files (or any other file for that matter) then you can the powershell simply loop over them :
PS C:\>foreach ($file in (Get-ChildItem -Path \\server1\Docs -Recurse -Force |
where {!$_.PSIsContainer} |
Where-Object {$_.Extension -eq ".xls"})) {
Protect-RMSFile -File $file.PSPath -InPlace -DoNotPersistEncryptionKey All -TemplateID "e6ee2481-26b9-45e5-b34a-f744eacd53b0" -OwnerEmail "IT#Contoso.com"
}
https://learn.microsoft.com/en-us/powershell/module/azureinformationprotection/protect-rmsfile?view=azureipps
I want this script to choose the latest file from a folder and then send it via ftp to the server.
I think it is choosing the file late because there is a new file on the FTP after running it. However it crashes constantly showing
uploading .....
uploading .....
uploading .....
$Dir="C:/log1"
$ftp = "ftpftpftp"
$user = "useruseruser"
$pass = "passpasspass"
$latest = Get-ChildItem -Path $Dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
for($latest){
"Uploading $latest..."
$uri = New-Object System.Uri($ftp+$latest.Name)
$webclient.UploadFile($uri, $latest.FullName)
}
I think you are using the wrong code block by accident. Currently you have created an infinite loop as you have no condition to how the for will exit.
A simple example of such a loop would be
for(){"Hello? Is it me you are looking for?"}
It should be structured like this
for (initialization; condition; repeat){code block}
an example would be
for($index =1; $index -lt 6;$index++){$index}
There is no need for that code block at all as long as $Dir is not empty. What you can do for a little error prevention is if($latest){} which will only work if $latest contains a file (in this code structure).
if($latest){
"Uploading $latest..."
$uri = New-Object System.Uri($ftp+$latest.Name)
$webclient.UploadFile($uri, $latest.FullName)
}
Your sample output does not have a file name in it so I suspect your $dir contains no files?
I am trying to write a script that will get the DATETAKEN attribute from a photo and create a folder structure based on that and move the file to this new location. I have found scripts on google that I'm trying to use but when I running it, it returns:
PS C:\Temp> C:\Temp\MovePhoto.ps1
GAC Version Location
--- ------- -------- True v4.0.30319
C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0....
Move-Item : The process cannot access the file because it is being
used by another process. At C:\Temp\MovePhoto.ps1:43 char:5
+ Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\NoBackup\TES...RA\IMG_1372.JPG:FileInfo) [Move- Item],
IOException
+ FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
If I do the script without the SystemDrawing line it works. But then I can't get the DATETAKEN attribute. I just can't figure out what I am missing.
Here is the script
[reflection.assembly]::LoadWithPartialName("System.Drawing")
$FileAll = (Get-ChildItem $SourcePath -Recurse | where {!$_.psiscontainer} | Select-Object Name,Fullname,BaseName,Extension,CreationTime,LastWriteTime,Length,#{Name="MD5";Expression={Get-Md5Hash $_.fullname}} | group MD5 | Where {$_.Count -gt 1 } | %{$_.Group} | sort MD5)
foreach ($File in $FileAll) {
$FileBaseName = $File.BaseName
$FileExtension = $File.Extension
$FileFullName = $File.FullName
$FileBaseNameNu = $FileBaseName + $FileExtension
$FileName = $File.Name
}
$foo = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $FileFullName
$date = $foo.GetPropertyItem(36867).Value[0..9]
$arYear = [Char]$date[0],[Char]$date[1],[Char]$date[2],[Char]$date[3]
$arMonth = [Char]$date[5],[Char]$date[6]
$arDay = [Char]$date[8],[Char]$date[9]
$strYear = [String]::Join("",$arYear)
$strMonth = [String]::Join("",$arMonth)
$strDay = [String]::Join("",$arDay)
$DateTaken = $strYear + "-" + $strMonth + "-" + $strDay
$FileLastWriteTime = $File.LastWriteTime
$FileDirYear = $FileLastWriteTime.Year
$FileDirDate = $FileLastWriteTime.ToShortDateString()
$FileDirFull = "$DestinationPath\DUBLETTER\$FileDirYear\$DateTaken"
# Create destination path
if ((Test-Path $FileDirFull) -eq $false) {
New-Item -Path $FileDirFull -ItemType Directory
}
if (Test-Path (Join-Path $FileDirFull $File.Name)) {
$n = 0
while ((Test-Path (Join-Path $FileDirFull $FileBaseNameNU)) -eq $true){
$FileBaseNameNU = $FileBaseName + "-" + ++$n + $FileExtension
}
}
Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"
}
Can you try to replace
[reflection.assembly]::LoadWithPartialName("System.Drawing")
by
Add-Type -AssemblyName "system.drawing"
Forget it, your trouble is with your file C:\NoBackup\TES...RA\IMG_1372.JPG wich can't be moved because it's open (seems to be open for usage in $foo var). Try first to copy it. You perhaps can use $foo.Dispose() before Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"
I've done this in VBScript, using GetDetailsOf()... see MSDN
This can be done using PowerShell, see... Scripting Guys
There may be a .NET method way of doing this, i.e.: more "native" to PS.
I am trying to use PowerShell to do a batch conversion of Word Docx to PDF - using a script found on this site:
http://blogs.technet.com/b/heyscriptingguy/archive/2013/03/24/weekend-scripter-convert-word-documents-to-pdf-files-with-powershell.aspx
# Acquire a list of DOCX files in a folder
$Files=GET-CHILDITEM "C:\docx2pdf\*.DOCX"
$Word=NEW-OBJECT –COMOBJECT WORD.APPLICATION
Foreach ($File in $Files) {
# open a Word document, filename from the directory
$Doc=$Word.Documents.Open($File.fullname)
# Swap out DOCX with PDF in the Filename
$Name=($Doc.Fullname).replace("docx","pdf")
# Save this File as a PDF in Word 2010/2013
$Doc.saveas([ref] $Name, [ref] 17)
$Doc.close()
}
And I keep on getting this error and can't figure out why:
PS C:\docx2pdf> .\docx2pdf.ps1
Exception calling "SaveAs" with "16" argument(s): "Command failed"
At C:\docx2pdf\docx2pdf.ps1:13 char:13
+ $Doc.saveas <<<< ([ref] $Name, [ref] 17)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Any ideas?
Also - how would I need to change it to also convert doc (not docX) files, as well as use the local files (files in same location as the script location)?
Sorry - never done PowerShell scripting...
This will work for doc as well as docx files.
$documents_path = 'c:\doc2pdf'
$word_app = New-Object -ComObject Word.Application
# This filter will find .doc as well as .docx documents
Get-ChildItem -Path $documents_path -Filter *.doc? | ForEach-Object {
$document = $word_app.Documents.Open($_.FullName)
$pdf_filename = "$($_.DirectoryName)\$($_.BaseName).pdf"
$document.SaveAs([ref] $pdf_filename, [ref] 17)
$document.Close()
}
$word_app.Quit()
The above answers all fell short for me, as I was doing a batch job converting around 70,000 word documents this way. As it turns out, doing this repeatedly eventually leads to Word crashing, presumably due to memory issues (the error was some COMException that I didn't know how to parse). So, my hack to get it to proceed was to kill and restart word every 100 docs (arbitrarily chosen number).
Additionally, when it did crash occasionally, there would be resulting malformed pdfs, each of which were generally 1-2 kb in size. So, when skipping already generated pdfs, I make sure they are at least 3kb in size. If you don't want to skip already generated PDFs, you can delete that if statement.
Excuse me if my code doesn't look good, I don't generally use Windows and this was a one-off hack. So, here's the resulting code:
$Files=Get-ChildItem -path '.\path\to\docs' -recurse -include "*.doc*"
$counter = 0
$filesProcessed = 0
$Word = New-Object -ComObject Word.Application
Foreach ($File in $Files) {
$Name="$(($File.FullName).substring(0, $File.FullName.lastIndexOf("."))).pdf"
if ((Test-Path $Name) -And (Get-Item $Name).length -gt 3kb) {
echo "skipping $($Name), already exists"
continue
}
echo "$($filesProcessed): processing $($File.FullName)"
$Doc = $Word.Documents.Open($File.FullName)
$Doc.SaveAs($Name, 17)
$Doc.Close()
if ($counter -gt 100) {
$counter = 0
$Word.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Word)
$Word = New-Object -ComObject Word.Application
}
$counter = $counter + 1
$filesProcessed = $filesProcessed + 1
}
This works for me (Word 2007):
$wdFormatPDF = 17
$word = New-Object -ComObject Word.Application
$word.visible = $false
$folderpath = Split-Path -parent $MyInvocation.MyCommand.Path
Get-ChildItem -path $folderpath -recurse -include "*.doc" | % {
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
$doc = $word.documents.open($_.fullname)
$doc.saveas($path, $wdFormatPDF)
$doc.close()
}
$word.Quit()
Neither of the solutions posted here worked for me on Windows 8.1 (btw. I'm using Office 365). My PowerShell somehow does not like the [ref] arguments (I don't know why, I use PowerShell very rarely).
This is the solution that worked for me:
$Files=Get-ChildItem 'C:\path\to\files\*.docx'
$Word = New-Object -ComObject Word.Application
Foreach ($File in $Files) {
$Doc = $Word.Documents.Open($File.FullName)
$Name=($Doc.FullName).replace('docx', 'pdf')
$Doc.SaveAs($Name, 17)
$Doc.Close()
}
I've updated this one to work on latest office :
# Get invocation path
$curr_path = Split-Path -parent $MyInvocation.MyCommand.Path
# Create a PowerPoint object
$ppt_app = New-Object -ComObject PowerPoint.Application
#$ppt.visible = $false
# Get all objects of type .ppt? in $curr_path and its subfolders
Get-ChildItem -Path $curr_path -Recurse -Filter *.ppt? | ForEach-Object {
Write-Host "Processing" $_.FullName "..."
# Open it in PowerPoint
$document = $ppt_app.Presentations.Open($_.FullName,0,0,0)
# Create a name for the PDF document; they are stored in the invocation folder!
# If you want them to be created locally in the folders containing the source PowerPoint file, replace $curr_path with $_.DirectoryName
$pdf_filename = "$($curr_path)\$($_.BaseName).pdf"
# Save as PDF -- 17 is the literal value of `wdFormatPDF`
#$opt= [Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType]::ppSaveAsPDF
$document.SaveAs($pdf_filename,32)
# Close PowerPoint file
$document.Close()
}
# Exit and release the PowerPoint object
$ppt_app.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ppt_app)