Powershell script halting and not working as expected - debugging

I have written a script to get system variables and copy of several folders ,I wanted to create a directory for copy of several folders,to prevent duplication of folders we wanted a check condition so each time we run the script it is not creating folders. Like an example
$nfle=New-Item -ItemType "Directory" -Path "D:\Temp\" -Name "foo"
[bool]$checkfle=Test-Path "D:\Temp\foo" -PathType Any
if ( $checkfle -eq $True)
{
Write-Output "$nfle Exists"
}
else
{
$bnfle=New-Item -ItemType "Directory" -Path "D:\Temp\" -Name ("boo")
}
$cpypste=Copy-Item "D:\Temp\foo" -destination "D:\Temp\boo"
Write-Host "Succesful Copy of Folders"
So when we run the script it is creating folder foo,again when we run the script , it is displaying foo exists, and stopping the script is not going to next line, not even displaying the message.Is there a way in powershell to find out why the script is stopping or shall i add more information statements. TIA

It best to start with test-path to see if the folder is there. A "Container" is a folder/directory. Then check if you need to write the folder.
# This should allow your script to continue of error.
$ErrorActionPreference = "Continue"
# check if "C:\Temp\Foo" exist. if not make C:\Temp\foo"
$nfle = 'C:\Temp\foo'
[bool]$checkfle = Test-Path $nfle -PathType Container
if ( $checkfle -eq $True)
{
Write-Output "$nfle Exists"
}
else
{
New-Item -ItemType "Directory" -Path "C:\Temp\" -Name "foo"
}
# check if "C:\Temp\boo" exist. if not make C:\Temp\boo"
$BooFilePath = "C:\Temp\boo"
[bool]$checkboo = Test-Path $BooFilePath -PathType Container
if ( $checkboo -eq $True)
{
Write-Output " $BooFilePath Exists"
}
else
{
New-Item -ItemType "Directory" -Path "C:\Temp\" -Name "boo"
}
# This makes the folder C:\Temp\boo\foo.
# $cpypste = Copy-Item -Path "C:\Temp\foo\" -destination "C:\Temp\boo\"
# If you want copy the contents of foo into boo you will need * or -recurse
$cpypste = Copy-Item -Path "C:\Temp\foo\*" -destination "C:\Temp\boo\" -PassThru
Write-Host "Succesful Copy of Folders"
$cpypste.FullName

I have tried the demo provided and it works from my side, multiple times, so I was not able to re-create the problem.
If you would like to debug scripts in PowwerShell, you may follow this link:
https://learn.microsoft.com/en-us/powershell/scripting/components/ise/how-to-debug-scripts-in-windows-powershell-ise?view=powershell-6
I am not sure, why you are storing the result of Copy-Item into a variable, as it is null?
Hope it helps!

Related

Powershell - Move Images in Subdirectories + Maintain Directory Structure

I want to move all images in a directory, including subdirectories, to a new location while maintaining the existing folder structure.
Following the example, here, I put the objects into a variable, like so:
$picMetadata = Get-FileMetaData -folder (Get-childitem K:\myImages -Recurse -Directory).FullName
The move must be based on the results of a logical expression, such as the following for example.
foreach ($test01 in $picMetadata) {
if ($test01.Height -match "^[0-9]?[0-9] ") {
Write-Host "Test01.Height:" $test01.Height
}
}
Still at an early testing phase So far, I'm having no success even testing for the desired files. In the example above, I thought this simple regex test might provide for anything from "1 pixels" to "99 pixels", which would at least slim down my pictures collection (e.g. an expression without the caret, like "[0-9][0-9] " will return "NN pixels" as well as "NNN Pixels", "NNNNNN pixels", etc.)
Once I figure out how to find my desired images based on a logical, image object dimensions test, I will then need to create a script to move the files. Robocopy /MOV would be nice, but i'm probably in over my head already.
I was going to try to base it on this example (which was provided to a User attempting to COPY (not move / copy/delete) *.extension files). Unfortunately, such a simple operation will not benefit me, as I wish to move .jpg,.png,.gif, etc, based on dimensions not file extension:
$sourceDir = 'K:\myImages\'
$targetDir = ' K:\myImages_psMoveTest\'
Get-ChildItem $sourceDir -filter "*" -recurse | `
foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Copy-Item $_.FullName -destination $targetFile
}
Perhaps you have a powershell script that could be used for my intended purpose? I'm just trying to move smaller images out of my collection, without having to overwrite same name images, and lose folder structure, etc.
Thank you very much for reading, and any advisory!
(Edit: Never opposed to improving Powershell skill, if you are aware of a freeware software which would perform this operation, please advise.)
If I understand your question correctly, you want to move image files with a pixel height of 1 up to 99 pixels to a new destination folder, while leaving the subfolder structure intact.
If that is true, you can do:
# needed to use System.Drawing.Image
Add-Type -AssemblyName System.Drawing
$sourceDir = 'K:\myImages'
$targetDir = 'K:\myImages_psMoveTest'
Get-ChildItem $sourceDir -File -Recurse | ForEach-Object {
$file = $_.FullName # need this for when we hit the catch block
try {
# Open image file to determine the pixelheight
$img = [System.Drawing.Image]::FromFile($_.FullName)
$height = $img.Height
# dispose of the image to remove the reference to the file
$img.Dispose()
$img = $null
if ($height -ge 1 -and $height -le 99) {
$targetFolder = Join-Path -Path $targetDir -ChildPath $_.DirectoryName.Substring($sourceDir.Length)
# create the target (sub) folder if it does not already exist
$null = New-Item -Path $targetFolder -ItemType Directory -Force
# next move the file
$_ | Move-Item -Destination $targetFolder -ErrorAction Stop
}
}
catch {
Write-Warning "Error moving file '$file': $($_.Exception.Message)"
}
}

Extract Specific Filetypes From Multiple Zips to one Folder in Powershell

I have Several zip files that Contain multiple filetypes. The ONLY ones I am interested in are the .txt files. I need to extract the .txt files only and place them in a folder of their own, ignoring all other file types in the zips files.
All the zip files are in the same folder.
Example
-foo.zip
--1.aaa
--2.bbb
--3.ccc
--4.txt
-foo2.zip
--5.aaa
--6.bbb
--7.ccc
--8.txt
I want to have 4.txt and 8.txt extracted to another folder. I can't for the life of my figure it out and spent ages looking, googling and trying. Even managing to delete to zips once in a while :-)
Thanks in advance
Use the ZipArchive type to programmatically inspect the archive before extracting:
Add-Type -AssemblyName System.IO.Compression
$destination = "C:\destination\folder"
# Locate zip file
$zipFile = Get-Item C:\path\to\file.zip
# Open a read-only file stream
$zipFileStream = $zipFile.OpenRead()
# Instantiate ZipArchive
$zipArchive = [System.IO.Compression.ZipArchive]::new($zipFileStream)
# Iterate over all entries and pick the ones you like
foreach($entry in $zipArchive.Entries){
if($entry.Name -like '*.txt'){
# Create new file on disk, open writable stream
$targetFileStream = $(
New-Item -Path $destination -Name $entry.Name -ItemType File
).OpenWrite()
# Open stream to compressed file, copy to new file stream
$entryStream = $entry.Open()
$entryStream.BaseStream.CopyTo($targetFileStream)
# Clean up
$targetFileStream,$entryStream |ForEach-Object Dispose
}
}
# Clean up
$zipArchive,$zipFileStream |ForEach-Object Dispose
Repeat for each zip file.
Note that the code above has very minimal error-handling, and is to be read as an example
Try this:
Set-Location "Extraction path"
#("full path of foo.zip","full path of foo2.zip") | ForEach {
& "Full path of 7z.exe" x '-i!*.txt' $_.FullName
}
Sets location to the path where files will be extracted.
Passes a array of zip files to for loop.
Exexute 7z command to extract only zip files.
Here is one approach:
Go through each .zip file in a folder.
Extract archive into separate folder.
Extract .txt file from folder.
Copy files into destination folder containing all .txt files. This will overwrite files if they already exist in the destination folder.
Cleanup extracted folders once finished.
Demo:
function Copy-ZipArchiveFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateScript({
if (-not(Test-Path $_ -PathType Container))
{
throw "The source path $_ does not exist. Please enter a valid source path."
}
else
{
$true
}
})]
[string]$Path,
[Parameter(Mandatory=$true)]
[ValidateScript({
if ([string]::IsNullOrEmpty($_.Trim()))
{
throw "The Destination path is null or empty. Please enter a valid destination path."
}
else
{
$true
}
})]
[string]$Destination,
[Parameter(Mandatory=$false)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string[]]$Include
)
# Create destination folder if it doesn't already exist
if (-not(Test-Path -Path $Destination -PathType Container))
{
try
{
New-Item -Path $Destination -ItemType Directory -ErrorAction Stop
}
catch
{
throw "The destination path $Destination is invalid. Please enter a valid destination path."
}
}
# Go through each .zip file
foreach ($zipFile in Get-ChildItem -Path $Path -Filter *.zip)
{
# Get folder name from zip file w/o .zip at the end
$zipFolder = Split-Path $zipFile -LeafBase
# Get full folder path
$folderPath = Join-Path -Path $Path -ChildPath $zipFolder
# Expand .zip file into folder if it doesn't exist
if (-not(Test-Path -Path $folderPath -PathType Container))
{
Expand-Archive -Path $zipFile.FullName -DestinationPath $folderPath
}
# Copy files into destination folder
foreach ($file in Get-ChildItem $folderPath -Include $Include -Recurse)
{
Copy-Item -Path $file.FullName -Destination $Destination
}
# Delete extracted folders
Remove-Item -Path $folderPath -Recurse -Force
}
}
Usage:
Copy-ZipArchiveFiles `
-Path "C:\path\to\zip\files" `
-Destination "C:\path\to\text\files" `
-Include "*.txt"
Note: Could also use this for multiple extension types as well by passing -Include *.txt, *.pdf. I also went a bit overboard in the parameter error checking, but I believe in writing robust code. Good habit to get into when writing your own cmdlets anyways :)

Folder deleting after script ends

I am currently writing a script that takes a folder of files, moves the first file to a folder with a specific name, then move the rest to another folder with a number for a name.
My script works however it also moves the folder and renames it too. Which section of the code is causing this?
$path = "C:\Users\User1\Desktop\MergeTest\_First\"
$FileCount = Get-ChildItem -Path $path -File | Measure-Object | %{$_.Count}
$FirstFile = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
$FinalReport = "C:\Users\User1\Desktop\MergeTest\___Final\TestOutput.xlsx"
Move-Item "C:\Users\User1\Desktop\MergeTest\_First\$FirstFile" $FinalReport
$Counter = 0;
Write-host $FileCount
for($Counter = 0; $Counter -lt $FileCount; $Counter++)
{
$FileInWork = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
move-item "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork" "C:\Users\User1\Desktop\MergeTest\__Second\$Counter.xlsx"
Write-host "File Moved"
}
What you could do is specify the -Include *.txt condition to your move-item commands so it is only to move just .txt, .log, or whatever file type you're moving and leave the folder how it is.
I believe your code could do with some cleaning up. Now you are executing Get-ChildItem 3 times, where using it once is enough.
Also, you should try and use the Join-Path rather than constructing the path and filenames yourself.
Especially where you do "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork", you should realize that Get-ChildItem returns FileInfo and/or DirectoryInfo objects; not strings.
Anyway, the below code should do what you want:
# define the path where all other paths are in
$rootPath = "C:\Users\User1\Desktop\MergeTest"
# create the working paths using the common root folder path
$filesPath = Join-Path -Path $rootPath -ChildPath '_First'
$firstDestination = Join-Path -Path $rootPath -ChildPath '___Final'
$secondDestination = Join-Path -Path $rootPath -ChildPath '__Second'
# test if the destination folders exist and if not create them
if (!(Test-Path -Path $firstDestination -PathType Container)) {
Write-Host "Creating folder '$firstDestination'"
$null = New-Item -Path $firstDestination -ItemType Directory
}
if (!(Test-Path -Path $secondDestination -PathType Container)) {
Write-Host "Creating folder '$secondDestination'"
$null = New-Item -Path $secondDestination -ItemType Directory
}
# get an array of all FileInfo objects in $filesPath
# you could consider adding -Filter '*.xlsx' here..
$allFiles = Get-ChildItem -Path $filesPath -Force -File
Write-Host 'Total number of files found: {0}' -f $allFiles.Count
# move the files
for ($i = 0; $i -lt $allFiles.Count; $i++) {
if ($i -eq 0) {
# the first file should go in the $firstDestination folder with specified name
$target = Join-Path -Path $firstDestination -ChildPath 'TestOutput.xlsx'
}
else {
# all other files go to the $secondDestination folder
# each file should have the index number as name
$target = Join-Path -Path $secondDestination -ChildPath ('{0}.xlsx' -f ($i + 1))
}
$allFiles[$i] | Move-Item -Destination $target -Force -WhatIf
}
Hope that helps
Remove the -WhatIf if you are satisfied with whatever the output on console shows.
P.S. I really think you should edit your question and change its title, because nothing in the question has to do with Folder deleting after script ends..

Powershell script to create text files in all the drives

I am new to PowerShell and looking for some help. Here I am trying to create a text file in all the drives but seems I am missing something here, any help is appreciated:
$drives = get-psdrive -p "FileSystem"
foreach ($drive in $drives)
{
New-Item -Path '$drive:\IO.txt' -ItemType File
}
Also, have another query regarding the same that how I can exclude particular drives e.g. "A:", "C:", "D:" drives?
Thanks
I think this is more what you are after.
$drives = get-psdrive -p "FileSystem"
$exclude = "C","D"
foreach ($drive in $drives) {
# Exclamation (!) is the same as -not meaning if not $true > Do the thing
If(!$exclude.Contains($drive.Name)) {
New-Item -Path "$($drive):\IO.txt" -ItemType File
}
}
Give New-Item -ItemType File -Name "Filename.ext" a shot,
see: https://mathieubuisson.github.io/powershell-linux-bash/
A more PowerShell like way is to filter the drives with a Where-Object and
do it in a single pipe
Get-PSDrive -PSProvider "FileSystem" |
Where-Object Name -notmatch 'A|C|D' |
New-Item -Path {"$($_.Name):\IO.txt"} -ItemType File -WhatIf
If the output looks OK remove the trailing -WhatIf

What's wrong with my Powershell script?

I don't understand this error message I'm receiving when I try to run my powershell script. The purpose is to copy a .bat file into the main win 7 startup folder on a series of machines.
And the script I am running.
$ServerList = Get-Content "C:\ServersList.txt" #Change this to location of servers list
$SourceFileLocation = "C:\firefox_issue.bat" #For example: D:\FoldertoCopy\ or D:\file.txt
$Destination = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup" #Example: C$\temp
foreach ($_ in $ServerList)
{Copy-Item $SourceFileLocation -Destination \\$_\$Destination -Recurse -PassThru}
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host
Write-Host "A"
Write-Host "B"
Write-Host "C"
Because your location is getting set to:
\\SERVERNAME\C:\ProgramData...
and it should be:
\\SERVERNAME\C$\ProgamData...
Your destination needs to be:
$Destination = 'C$\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
And your loop should be:
foreach($server in $serverList) {
Copy-Item $SourceFileLocation -Destination "\\$server\$Destination" -Recurse
}
You should probably avoid explicitly using $_ as a variable name as $_ is a special variable for accessing an object in the pipeline.
Did you read the comment behind the $Destination line?
This is a UNC path.
\\server1\c:\programdata\ is not a valid UNC-path. Try:
$Destination = "C$\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
Also, $_ is a reserved variable for pipeline input, so you need to change it, like:
foreach ($server in $ServerList)
{Copy-Item $SourceFileLocation -Destination \\$server\$Destination -Recurse -PassThru}

Resources