Powershell Save File In Original Folder When Recursing Through Folders - powershell

I am attempting to use a Powershell Script I found on here to convert PDF files into TIFF files. I have the majority of the script working but I can't seem to figure out how to have the TIFF files saved in the original folder that the PDF was in.
#Path to your Ghostscript EXE
$tool = 'C:\Program Files\gs\gs9.25\bin\gswin64c.exe'
#Directory containing the PDF files that will be converted
$inputDir = 'C:\Temp\Test_ED_Data\1\'
#Output path where converted PDF files will be stored
$outputDirPDF = 'C:\Temp\PDF_Out\'
#Output path where the TIFF files will be saved
$outputDir = $inputDir
$pdfs = get-childitem $inputDir -recurse | where {$_.Extension -match "pdf"}
foreach($pdf in $pdfs)
{
$tif = $outputDir + $pdf.BaseName + ".tiff"
if(test-path $tif)
{
"tif file already exists " + $tif
}
else
{
'Processing ' + $pdf.Name
$param = "-sOutputFile=$tif"
& $tool -q -dNOPAUSE -sDEVICE=tiffg4 $param -r300 $pdf.FullName -c quit
}
Move-Item $pdf $outputDirPDF
}
After the script runs, all the PDFs show up in the original input directory and not in any sub directories.
So for example, if C:\Temp\Test_ED_Data\1\ has 1 PDF in it, it gets converted to TIFF and saved in C:\Temp\Test_ED_Data\1\, but any PDFs in a sub directory such as C:\Temp\Test_ED_Data\1\Progress\ also get saved in the original sub directory of C:\Temp\Test_ED_Data\1.
How do I get this script to make sure the converted files are saved in the directory they are retrieved from? It seems as if Powershell doesn't remember the recursed path when it refers to the section $outputDir = $inputDir. What do I need to do to correct that?
Thank you.

So a quick look at the scripts shows
$outputDir = $inputDir
But also
$pdfs = get-childitem $inputDir -recurse | where {$_.Extension -match "pdf"}
This basically means find all files with a PDF extension in $inputDir and search all other folders inside $inputDir. But where you save is a static location of the $inputDir
Give this a shot
function CovertPDF-TIFF($InputDirectory, $OutputPDFLocation){
$tool = 'C:\Program Files\gs\gs9.25\bin\gswin64c.exe'
get-childitem $InputDirectory -include "*.pdf" -recurse | %{
$tiff = "$($_.Directory)\$($_.BaseName).tiff"
if(test-path $tiff)
{
"tiff file already exists " + $tiff
}
else
{
'Processing ' + $_.Name
$param = "-sOutputFile=$tiff"
& $tool -q -dNOPAUSE -sDEVICE=tiffg4 $param -r300 $_.FullName -c quit
}
Move-Item $pdf $OutputPDFLocation
}
}
CovertPDF-TIFF -InputDirectory C:\Temp\Test_ED_Data\1\ -OutputPDFLocation C:\Temp\PDF_Out\
Whats happening here is piping. Piping is basically where you take the output and push it to another command the symbol for pipe is |.
In the Get-ChildItem we will -include all files that end in *.pdf
We Pipe | each item to a foreach-object otherwise known as %
There we create a variable $Tiff to store where and what name to call the tiff based on the PDF we found. In a pipe the $_ is the variable for the information that was piped (in this case its the Child Item Information aka the PDF file information). In powershell $() allows you to add separate commands to a string or another command called a expression its proper name is a Sub Expression. So $Tiff holds the string of the Child Item Directory then adds the Child Item File name then adds the .tiff to the end.
It then checks to see if the Item exists with the command Test-Path.
If it does then it returns a message. If it doesnt then it creates the parameters and runs the gswin64c.exe executable. In one of the Parameters you will see -sOutputFile=$tiff This is where we difine where the new Tiff file will be saved. Lastly the Move the PDF file to a new location with Move-Item

All you need to do is, exchange this line:
$tif = $outputDir + $pdf.BaseName + ".tiff"
with this:
$tif = $pdf.FullName -Replace $pdf.Extension,".tiff"

Related

Copy and paste files through clipboard in Powershell

In the folder E:\Files there are 2 files, which I want to copy and paste into D:\Dest:
E:\Files\
File1.txt
File2.txt
Using the keyboard I would simply select the 2 files, push ctrl+c, and in the destination folder D:\Dest\ I then push ctrl+v.
Now I want to achieve this using Powershell. So I copy the files into the clipboard:
Set-Clipboard -Path E:\Files\*
But how to paste those files now into the destination folder? Obviously, I will need Get-Clipboard. But it's not quite clear to me, how to use it, in order to paste the files.
I know I could copy the contents of these 2 files and then by using Set-Content create those files in D:\Dest\ on my own and copy the content into them. But is there any direct way? Because with Set-Clipboard those files already are in the clipboard. They just need to be pasted. I can use ctrl+v and it works. But I want to paste them through Powershell. Any ideas?
Here's the script I use to paste files/directories:
$fileDrop = get-clipboard -Format FileDropList
if($fileDrop -eq $null)
{
write-host "No files on the clipboard"
return
}
foreach($file in $fileDrop)
{
if($file.Mode.StartsWith("d"))
{
$source = join-path $file.Directory $file.Name
$e = "copy-item -Recurse $source $($file.Name)"
$e
Invoke-Expression $e
}
else
{
$file.Name
$file | copy-item
}
}
You could use the .NET Clipboard class:
Add-Type -AssemblyName System.Windows.Forms
$files = [System.Collections.Specialized.StringCollection]::new()
$files.Add('YOUR_FILE_PATH_HERE')
[System.Windows.Forms.Clipboard]::SetFileDropList($files)
This copies YOUR_FILE_PATH_HERE to the local clipboard.
You could use this, for example, when you're in a local terminal and need to copy file(s) to a remote session without PowerShell remoting (e.g., RDP or Citrix) without opening the directory in Explorer.
It's a little unwieldy, so if I used it a lot, I'd probably put it in a function like Copy-FileToClipboard and add it to my PowerShell profile.
Inspired by the other comment in this thread, but refactored, and supporting both move and copy actions (moving is the default action, while copying is achieved with the -c parameter). The script pastes clipboard files and folders into the current directory (I have placed it in a PATH directory and use it as paste).
$data = Get-Clipboard -Format FileDropList
if ($data -ne $null)
{
foreach ($file in $data)
{
if ($args[0] -eq "-c")
{
$action = "Copied:"
Copy-Item -Recurse -LiteralPath $file
}
else
{
$action = "Moved:"
Move-Item -LiteralPath $file
}
$action
"- from: $(Join-Path $file.Directory $file.Name)"
"- into: $(Join-Path "$(Get-Location)" $file.Name)"
}
}
The -LiteralPath parameter was needed to handle filenames containing square brackets.

foreach (#file in $files){ nul find "2019" $file save(D:\Export2019\$file) }

.ps1 copy files to a specific locations if specific text in file
i`m trying a code in Powershell ...
I have a lot of .xml files in S:\Export\ and i want to copy the files that contains the text "2019" in the folder S:\Export2019\
This is my code:
Start-Transcript -Path S:\Export2019\info.txt
$files = Get-ChildItem "S:\Export\"
mkdir S:\Export2019
foreach ($file in $files){
>nul find "<APPDATE>2019" $file (
echo $file was found.
Save("S:\Export2019\$file")
)
}
ii S:\Export2019 #
I have a lot of .xml files in S:\Export\ and i want to copy the files that contains the text "2019" in the folder S:\Export2019\
this is not working:
>nul find "<APPDATE>2019" $file (
echo $file was found.
Save("S:\Export2019\$file")
I'm not exactly sure if I understood your question correctly. The following script will loop through all XML-files in a specific directory and searches for the text 2019. If that text is in the file, it will be copied into another directory
Please be aware that this script is a very rough and "brute force" approach but it should give you a basis to work with
$source_dir = ".\S_Export2019" # Directory where the XML files are
$target_dir = ".\Target_Directory" # Directory where "2019" files will be copied to
# Loop through the directory $source_dir and get the fullpath of all XML-files
foreach ($file in (Get-ChildItem "$source_dir\*.xml")) {
# Save the content of the XML file
$file_content = Get-Content $file -raw
# Check if the XML file contains "2019"
if ($file_content -match "2019") {
Write-Host "$file contains '2019'"
Copy-Item $file $target_dir # Copy file to $target_dir
}
}
Edit
Thanks #LotPings for the correction - I've added the -raw Parameter to the Get-Content and also changed the if-comparison to use -match rather than the former -contains

powershell : command does not work when called inside a loop

The following command does work in a powershell console
Restore-SvnRepository D:\temp\Backup\foo.vsvnbak
(Restore-SvnRepository is a command that comes with visualsvn, it expects a path or unc to a file to be restored as parameter)
As I need to execute this command for a large number of files (>500) I embedded it inside a powershell loop but then it doesn't work
$fileDirectory = "D:\temp\Backup"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in Get-ChildItem $fileDirectory)
{
$filePath = $fileDirectory + "\" + $file;
# escape string for spaces
$fichier = $('"' + $filepath + '"')
# write progress status
"processing file " + $fichier
# command call
Restore-SvnRepository $fichier
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
I don't understand why this does not work. The loop and filename looks good but when executed, each command throws the following error message
Restore-SvnRepository : Parameter 'BackupPath' should be an absolute or UNC path to the repository
backup file you would like to restore: Invalid method Parameter(s) (0x8004102F)
Could you help me?
EDIT
It looks like that I was confused by Get-ChildItem that return a System.IO.FileSystemInfo rather than a string.
I didn't notice because of a implicit call to the ToString() when writing to the console that made me think I was dealing with string (rather than FSI)
The following code works
$fileDirectory = "D:\temp\Backup\"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in $files)
{
# $file is an instance of System.IO.FileSystemInfo,
# which contains a FullName property that provides the full path to the file.
$filePath = $file.FullName
Restore-SvnRepository -BackupPath $filePath
}
$file isn't a string, it's an object containing file data.
You can simplify your code as follows:
$fileDirectory = "D:\temp\Backup"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in $files)
{
# $file is an instance of System.IO.FileSystemInfo,
# which contains a FullName property that provides the full path to the file.
$filePath = $file.FullName
# ... your code here ...
}

How to alter WinSCP script for recursively searching for text to print just a folder of the first match and exit

I want to be able to look for a text string in all folders on a remote FTP server. Once found, I need to know the name of the folder which contains the text document BUT not the name of the file.
How can I make changes to "Action on match" to make this script (WinSCP Extension Search recursively for text in remote directory / Grep files over SFTP/FTP protocol) run silently (without displaying anything in the terminal) and once the match is found simply stop and display ONLY the name of the folder (containing the file with the text string)? Also is it possible to display the result in red? Here is the "Action on match" portion of the script (I tried to include the whole thing but cannot do it for some reason).
I can do this with the help of the latest WinSCP custom Search for Text button (please see .ps1 script below which makes this search function possible). However instead of just stopping upon match, the search goes on until the last folder. What is worse is that in order to find the result I need to scroll all the way back up and check every entry. Only one of them will have the name of my folder listed so that is a long procedure.
{
# Action on match
# Modify the code below if you want to do another task with
# matching files, instead of grepping their contents
Write-Host ("File {0} matches mask, searching contents..." -f $fileInfo.FullName)
$tempPath = (Join-Path $env:temp $fileInfo.Name)
# Download file to temporary directory
$sourcePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName)
$transferResult = $session.GetFiles($sourcePath, $tempPath)
# Did the download succeeded?
if (!$transferResult.IsSuccess)
{
# Print error (but continue with other files)
Write-Host $transferResult.Failures[0].Message
}
else
{
# Search and print lines containing "text".
# Use -Pattern instead of -SimpleMatch for regex search
$matchInfo = Select-String -Path $tempPath -SimpleMatch $text
# Print the results
foreach ($match in $matchInfo)
{
Write-Host ($fileInfo.FullName + ":" + $match.LineNumber + ":" + $match.Line)
}
# Delete temporary local copy
Remove-Item $tempPath
}
}
{
# Action on match
# Modify the code below if you want to do another task with
# matching files, instead of grepping their contents
$tempPath = (Join-Path $env:temp $fileInfo.Name)
# Download file to temporary directory
$sourcePath = [WinSCP.RemotePath]::EscapeFileMask($fileInfo.FullName)
$transferResult = $session.GetFiles($sourcePath, $tempPath)
# Did the download succeeded?
if (!$transferResult.IsSuccess)
{
# Print error (but continue with other files)
Write-Host $transferResult.Failures[0].Message
}
else
{
# Use -Pattern instead of -SimpleMatch for regex search
$matchInfo = Select-String -Path $tempPath -SimpleMatch $text
Remove-Item $tempPath
if ($matchInfo)
{
# Print a path to the file's folder and exit
Write-Host ($fileInfo.FullName -replace "/[^/]*$")
exit
}
}
}

Powershell Move file to new destination based on trimmed file name

I have a folder where files get dropped, I wish to pull the files from that folder and move to a new folder based on part of the file name. If the new folder is missing then create it.
I have attempted to put together the below, however it throws an error about the path already existing and doesn't move the file.
File names can be any thing with out pattern except the last 16 characters of the file, I am removing these and using the remaining as the folder name.
I am really new to scripting so if i have made a silly mistake explanations are appreciated.
Edit
I have played with different orders of operations, added a "-Force" to the new item command, tried with using "Else" and not "If (!(".
I am now at the point where it proudly displays the new directory and then stops.
Could i add the move-item part to a new for each loop so it is processed after the dir is created and tested? If so how do you arrange the { } parts?
Edit 2
I finally have it working, updated script below, the movie-item command was having issues when running into special characters in file names, in my case it was square brackets. The -literalpath switch fixed that for me.
Thanks every one for your input.
Updated script 3.0
#Set source folder
$source = "D:\test\source\"
#Set destination folder (up one level of true destination)
$dest = "D:\test\dest\"
#Define filter Arguments
$filter = "*.txt"
<#
$sourcefile - finds all files that match the filter in the source folder
$trimpath - leaves $file as is, but gets just the file name.
$string - gets file name from $trimpath and converts to a string
$trimmedstring - Takes string from $trimfile and removes the last 16 char off the end of the string
Test for path, if it exists then move on, If not then create directory
Move file to new destination
#>
pushd $source
$sourcefile = Get-ChildItem $source -Filter $filter
foreach ($file in $sourcefile){
$trimpath = $file | split-path -leaf
$string = $trimpath.Substring(0)
$trimmedstring = $string.Substring(0,$string.Length-16)
If(!(Test-Path -path "$dest\$trimmedstring")){New-Item "$dest\$trimmedstring" -Type directory -Force}
move-Item -literalpath "$file" "$dest\$trimmedstring"
}
You may have to tweak the paths being used but the below should work.
$sourcefiles = ((Get-ChildItem $source -Filter $filter).BaseName).TrimEnd(16)
foreach ($file in $sourcefiles)
{
if(!(Test-Path "$dest\$file")){
New-item -ItemType directory -path "$dest\$file"
}
Move-Item "$source\$file" "$dest\file"
}
I finally have it working, updated script below, the movie-item command was having issues when running into special characters in file names, in my case it was square brackets. The -literalpath switch fixed that for me. Thanks every one for your input.
#Set source folder
$source = "D:\test\source\"
#Set destination folder (up one level of true destination)
$dest = "D:\test\dest\"
#Define filter Arguments
$filter = "*.txt"
<#
$sourcefile - finds all files that match the filter in the source folder
$trimpath - leaves $file as is, but gets just the file name.
$string - gets file name from $trimpath and converts to a string
$trimmedstring - Takes string from $trimfile and removes the last 16 char off the end of the string
Test for path, if it exists then move on, If not then create directory
Move file to new destination
#>
pushd $source
$sourcefile = Get-ChildItem $source -Filter $filter
foreach ($file in $sourcefile){
$trimpath = $file | split-path -leaf
$string = $trimpath.Substring(0)
$trimmedstring = $string.Substring(0,$string.Length-16)
If(!(Test-Path -path "$dest\$trimmedstring")){New-Item "$dest\$trimmedstring" -Type directory -Force}
move-Item -literalpath "$file" "$dest\$trimmedstring"
}