I'm attempting to use powershell to batch convert a lot of docx into pdf, into a different directory while maintaining the folder structure of the root.
I have the script working, however around 1 out of every 10 documents word pops up a "SaveAs" dialog, which i do not understand prompting me to save the docx file, although i have visible set to false.
#Stage the files
$sourceDir = "C:\Documents\"
$targetDir = "C:\Temp_Stage\"
Get-ChildItem $sourceDir -filter "*.doc?" -recurse | foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Copy-Item $_.FullName -destination $targetFile
}
#Convert the files
$wdFormatPDF = 17
$word = New-Object -ComObject word.application
$word.visible = $false
$folderpath = "c:\Temp_Stage\*"
$fileTypes = "*.docx","*doc"
Get-ChildItem -path $folderpath -include $fileTypes -Recurse |
foreach-object {
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
$doc = $word.documents.open($_.fullname)
$doc.saveas([ref] $path, [ref]$wdFormatPDF)
$doc.close()
}
$word.Quit()
Is there a way to suppress all word dialogs / warning / errors it should be a fairly automatic process that has ended up being pretty manual process.
I found out that you should pause between the Word-COM commands.
I also had to write a script that Word converts the documents from. dot to. dotm.
Not only did I occasionally get the save dialog, but also a lot of E_FAIL errors in the console.
The breaks (maximum 50ms) helped a lot.
Break in Powershell:
Start-Sleep -m 50
I hope it will help you.
Greetings
Bloodrayne1995
Related
I checked many post here but could not find anything helpful.
If there is any, please direct me.
I am able to unzip the file but it is opening a pop up window which show the progress of unzipping. I want it to execute the steps silently.
below is my code:-
$filepath = Get-ChildItem -Path \\Srcfile\data -Filter *.zip -Recurse
$shell = new-object -com shell.application
foreach($file in $filepath)
{
$zip = $shell.NameSpace($file.FullName)
foreach($item in $zip.items())
{
$shell.Namespace($file.DirectoryName).copyhere($item)
}
Remove-Item $file.FullName
}
everyone. I'm trying to figure out a way to read the contents of a directory in Windows and find files of a specific file extension ('.hod' in this case), then create shortcuts to each of those files into 'C:\Users\Public\Desktop.'
Below is an example I've been testing with so far in PowerShell (I know it looks utterly terrible). I'd appreciate any input. Thanks.
$shortcutfiles = dir "C:\C:\IBM-Shortcuts\*.hod"
$DestinationDir = "C:\Users\Public\Desktop"
foreach ($shortcutfile in $shortcutfiles ) {
$TargetPath = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.FullName }
$BaseName = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.BaseName }
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$DestinationDir" & "$BaseName"+lnk)
$Shortcut.TargetPath = "$TargetPath"
$Shortcut.Save()
}
So a few things to note here:
Use Full cmdlet names instead of alias', this for clarity.
Get-Childitem instead of dir
... | Foreach-Object { ... instead of ... | % ...
Only iterate over the folders contents once and reference the $_ variable within the loop instead of looping within a loop
if a variable is only being used once then don't bother storing it in its own variable
$Destination is no longer used
Indent your code following basic formatting rules
As #mklement0 mentioned, it's worth executing $WshShell = New-Object -comObject WScript.Shell only once, before the pipeline.
$WshShell = New-Object -comObject WScript.Shell
Get-ChildItem "C:\IBM-Shortcuts\*.hod" | Foreach-Object {
$Shortcut = $WshShell.CreateShortcut("C:\Users\Public\Desktop\$($_.BaseName).lnk")
$Shortcut.TargetPath = $_.FullName
$Shortcut.Save()
}
Some other things to note:
On line 1 you are referencing "C:\C:\IBM-Shortcuts\*.hod", this has one too many C:\ in it.
Your use of $TargetPath = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.FullName } is not setting the targetpath for the current iteration of $Shortcutfile, it is returning a list of all file paths in "C:\IBMiACS-Shortcuts"
Take a look into the basics of a foreach loop here
I am currently working on finding a way to get a list with path of all the .xlsx and .xls file that are password protected in a network drive that contains tons and tons of folders and sub folders. I put together this script below that works fine, but it only returns .xlsx files, none of the .xls files with password protected were returned. I am wondering if anyone knows how to get the .xls file with password or any other script that would get this job done? Appreciate all your help!
Script
$path = "C:\Users\DC\Desktop"
$dest = "C:\Users\DC\Desktop\ExcelWithPassword.txt"
$Full = Get-ChildItem $path -Include *.xlsx*, *.xls* -Recurse -ErrorAction SilentlyContinue
$List = select-string -pattern "<encryption" $Full
foreach ($file in $List) {
$file.path | Out-File $dest -Append -Force
}
The output is basically a list of paths where those password protected files are located.
unless you have other files in the target directory tree, with an '.xl extension. Why are you doing this ...
Get-ChildItem $path -Include *.xlsx*, *.xls* -Recurse -ErrorAction SilentlyContinue
... you only need this...
Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue
If you are after just the full path, ask for it, using this ...
Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue |
Select-Object -Property Fullname
# Results
<#
FullName
--------
D:\Temp\NewFolder\Test.xlsx
D:\Temp\Test.xls
D:\Temp\Test.xlsx
#>
... or this.
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue).FullName
# Results
<#
D:\Temp\NewFolder\Test.xlsx
D:\Temp\Test.xls
D:\Temp\Test.xlsx
#>
As far as the loop, you can also shorten your code to something similar.
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue |
Select-Object -Property Fullname) -match '<encryption' |
Out-File $dest -Append -Force
Or
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue).FullName -match '<encryption' |
Out-File $dest -Append -Force
You are not saying how the files were encrypted. Excel allows for protecting the sheet, the workbook, etc. You can't check a password-protected file by searching for a string without opening the file. To open the file you must use the application interface to open the file. For Excel it's:
### Automate Excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook1 = $excel.Workbooks.Add()
$Sheet = $Excel.WorkSheets.Item(1)
$Sheet.Cells.Item(1,1) = "Hello from Powershell "
$Sheet.Cells.Item(1,2) = "Using VBA from Excel Button object"
Based on what you are after, there are a few other considerations you must have. Scanning and doing this across the whole network and thousands of files requires planning, and parallel processing.
I sniped this script online and it works fine for converting the files in the parent folder. It does not however iterate through the child folders. I do not get any errors and I have verified all the folder permissions are correct. Additionally, I have scripts that are coded similar for *.docx and *.pptx files and they run successfully. This one however is not working as expected. Any ideas?
$path = "c:\converted\"
$xlFixedFormat = "Microsoft.Office.Interop.Excel.xlFixedFormatType" -as [type]
$excelFiles = Get-ChildItem -Path $path -include *.xls, *.xlsx -recurse
$objExcel = New-Object -ComObject excel.application
$objExcel.visible = $false
foreach($wb in $excelFiles)
{
$filepath = Join-Path -Path $path -ChildPath ($wb.BaseName + ".pdf")
$workbook = $objExcel.workbooks.open($wb.fullname, 3)
$workbook.Saved = $true
"converted $wb.fullname"
$workbook.ExportAsFixedFormat($xlFixedFormat::xlTypePDF, $filepath)
$objExcel.Workbooks.close()
#get rid of conversion copy
#Remove-Item $wb.fullname
}
$objExcel.Quit()
$excelFiles will contain subfolders, but your construction of $filepath uses only the original $path and current $wb.BaseName without taking into account that the current $wb.FullName may contain a longer path.
Replace
$filepath = Join-Path -Path $path -ChildPath ($wb.BaseName + ".pdf")
with
$filepath = $wb.fullname -replace $wb.extension,".pdf"
I have a PowerShell script that moves all files from one location to another that have a date modified older than 3 years. I have it so the file when moved to the new location also keeps the file structure of the original.
I am trying to make it so once the file has been moved to the new location it creates a shortcut in the original directory which points to the new location of the file.
Below is my script so far which does all the above minus the shortcut.
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -Recurse |
Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$sourceDir")
$Shortcut.TargetPath = $destination
$Shortcut.Save()
}
In my script I have included my attempt at creating this shortcut but it hasn't helped. I have also read through the following but don't understand it too well..
How to create a shortcut using Powershell
Powershell Hard and Soft Links
Edit:
I have successfully got the shortcut to create and in the original folder. However, I can't seem to figure out how to pass a variable to use as the shortcut name. At the moment a string is hard coded, which is what the shortcut gets named. Please see code below: I would like to set the name as the item full name (Same name as document that was moved).
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -recurse | Where-Object {!$_.PsIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$wshshell = New-Object -ComObject WScript.Shell
$desktop = [System.Environment]::GetFolderPath('Desktop')
$lnk = $wshshell.CreateShortcut($sourceDir + "\ShortcutName.lnk")
$lnk.TargetPath = "$destination"
$lnk.Save()
}
.lnk files are fine when you're using Explorer but they don't play well in Powershell or a command prompt.
What you need to do is create a symbolic link for the file. You can't do this in Powershell, but there is a command line utility called mklink that does it. I've wrapped it in a function so that you can call it:
function CreateLink
{
param
(
[string] $LinkName,
[string] $TargetFile
)
&"cmd.exe" /c mklink "$LinkName" "$TargetFile" | Out-Null
}
In your example you would call it like this:
CreateLink -LinkName $item.FullName -TargetFile $destination
When you look at the directory in Powershell the file will show up as being 0 bytes in size. Don't worry about that.
Thanks for your script Android Magic.
I have modified it to:
Copy a set of files from source to destination
It creates the identical folder structure on the destination, even if the folders are empty
It then creates a symbolic link to the archived file. SymbolicLink support was added in Powershell v5.1. You have to run the script as Admin in order for the Symbolic Link creation to work.
I'd like to add a function to email if anything goes wrong and a summary of status, but that's for another day.
$sourceDir = "\\Fileserver1\IT\Vendor"
$archiveTarget = "\\FS-ARCHIVE\Archive\Fileserver1\IT\Vendor"
$rootArchivePath = "\\FS-ARCHIVE\Archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
# Copy folder structure to Archive
Get-ChildItem -Path $sourceDir -Recurse |
?{ $_.PSIsContainer } |
Copy-Item -Destination {Join-Path $archiveTarget $_.Parent.FullName.Substring($sourceDir.length)} -Force
$items = Get-ChildItem $sourceDir -Recurse -Attributes !Directory |
Where-Object {$_.LastAccessTime -le $date}
foreach ($item in $items)
{
$withoutRoot = Split-Path -Path $item.FullName
$destination = $rootArchivePath + $withoutRoot.Remove(0,1)
$destFile = $destination + "\" + $item
Move-Item -Force -Path $item.FullName -Destination $destination -Verbose
New-Item -ItemType SymbolicLink -Path $withoutRoot -Name $item -Value $destFile -Force -Verbose
}