Powershell File database comparison with drive letter variable input [duplicate] - database

This question already has an answer here:
Pass object[] into a function in PowerShell
(1 answer)
Closed 4 years ago.
I'm building a tool that will scan my files and a friend's files. We will use this to make sure we have the same files in our databases. The script I have so far has a variable input issue. For some reason, the PowerShell script fails on my drive letter input. Anyone have any ideas?
Here is my script:
{
function Show-Menu {
param (
[string] $Title = "Andy's Manual Database Tool"
)
Clear-Host
Write-Host ""
Write-Host "================ $Title ================"
Write-Host ""
Write-Host -f Green "1. Andys Files listing"
Write-Host -f green "2. Reids files listing"
Write-Host -f Red "3. Dark Matter Testing"
Write-Host "4. Convert .txt to .csv"
Write-Host "5. Convert Blank File to .csv"
Write-Host "6. Convert .csv to .txt"
}
Function Body {
Show-Menu
Write-Host ""
$Input = Read-Host "Please make a selection"
if ($Input -eq "1") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y | Select-Object -Property Name | Export-Csv -NoTypeInformation $z Andy.csv
}
if ($Input -eq "2") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. File Format Examples: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y | Select-Object -Property Name | Export-Csv -NoTypeInformation $z' From Reid.csv'
}
if ($Input -eq "3") { Get-Process | Stop-Process }
if ($Input -eq "4") {
Clear-Host
Get-ChildItem *.txt | rename-item -newname { $_.name -replace ".txt",".csv" }
}
if ($Input -eq "5") {
Clear-Host
Get-ChildItem * -Exclude *.ps1,*.CSV,*.TXT | rename-item -newname { "$($_.name).CSV" }
}
if ($Input -eq "6") {
Clear-Host
Get-ChildItem *.csv | rename-item -newname { $_.name -replace ".csv",".txt" }
}
Write-Host 'Complete! ^_^'
Start-Sleep -seconds 5
Body
}
Body
}
This issue is here, from the script above:
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y |
Select-Object -Property Name |
Export-Csv -NoTypeInformation $z Andy.csv
I'm using $root as an input for my drive letter or location path which is the problem.

There a few minor issues.
$z Andy.csv Needs to be changed to "$z Andy.csv". You will notice when you ran this before you would of received the following message:
Export-Csv : Cannot bind parameter 'Delimiter'. Cannot convert value
"Andy.csv" to type "System.Char". Error: "String must be exactly one
character long."
Get-ChildItem -Path $root -File -Recurse *.$y Needs to be changed to Get-ChildItem -Path "$root" -Recurse -Include "*$y" - You are prompting the user to put in file extension with .<extension> (assigned to $y) then you are trying to filter with $y
Ex:$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
Issue: "*.$y" this would equal "..extension". Your results would then be 0 (unless you had a file with ..jpg or something like that)
Corrected:
if ($Input -eq "1") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path "$root" -Recurse -Include "*$y" | Select Name | Export-Csv -NoTypeInformation "$z Andy.csv"
}
You should be able to correct the rest of your script following the "Corrected" example.
EDIT:
Credit to TheIncredible1
There are automatic variables that should never be used other than for their intended purpose. In your case $Input:
Contains an enumerator that enumerates all input that is passed to a
function. The $input variable is available only to functions and
script blocks (which are unnamed functions). In the Process block of a
function, the $input variable enumerates the object that is currently
in the pipeline. When the Process block completes, there are no
objects left in the pipeline, so the $input variable enumerates an
empty collection. If the function does not have a Process block, then
in the End block, the $input variable enumerates the collection of all
input to the function.
However, in this case it should not effect your outcome. But, it is a very bad habit to get into and is best to be avoided.

Related

How do I change my powershell script to GUI

I have recently started taking online Active Directory courses and in the process, I have developed a script for moving computers between OUs. The script is fine and running, but I was wondering if someone can help me change it to GUI(exe) file.
I have looked around and it does not seem there is a good place to provide a nice explanation. Code is below:
...script searches a computer given a keyword. user selects one of the computers listed after search. script then searches an OU based on a keyword input from user. Then, then uses chooses an option of the OUs listed after search. Script then moves the computer to that OU.
Import-Module ActiveDirectory
$computerName = Read-Host -Prompt 'Enter The Name of The Computer You Want To Move'
$temp = Get-ADComputer -filter "Name -like '*$computerName*'" -Properties DistinguishedName | Select-Object -Property DistinguishedName
#$comp[0].DistinguishedName.Split("=").Split(",")[1]
Function Select-Computer
{
$counter = 0
Write-Host "Select Item"
foreach ($c in $temp)
{
write-host "$counter :" $c.DistinguishedName.Split("=").Split(",")[1]
$Counter++
}
[ValidateNotNullOrEmpty()]
$Selection = Read-Host -Prompt 'Choose the computer you want to move'
return $temp[$Selection]
}
$returned = Select-Computer
$computerLocation = $returned.DistinguishedName
$keyword = Read-Host -Prompt "Enter a keyword of the OU you want to move to"
$find = Get-ADOrganizationalUnit -Filter "Name -like '*$keyword*'" -Properties DistinguishedName | Select-Object -Property DistinguishedName
Function Select-OU
{
$counter = 0
Write-Host "Select the OU"
foreach ($ou in $find)
{
$canonicalName = Get-ADOrganizationalUnit -Identity $ou.DistinguishedName -Properties CanonicalName | Select-Object CanonicalName
write-host "$counter :" $ou.DistinguishedName.Split("=").Split(",")[1] in "$canonicalName".Split("=").Replace("#", "").Replace("{", "").Replace("}", "").Replace("CanonicalName", "")
#DistinguishedName.Split("=").Replace("OU", "").Replace(",", "/")
#
$Counter++
}
[ValidateNotNullOrEmpty()]
$Selection = Read-Host -Prompt 'Choose the target you OU'
return $find[$Selection]
}
$return = Select-OU
$targetOU = $return.DistinguishedName
Move-ADObject -Identity "$computerLocation" -TargetPath "$targetOU" -Verbose

Loop though network share and list all users folders and create/delete file

We have 1 main network share named the below, after the underscore there is 8 locations which are "abc$", "def$", "ghi$", "jkl$", "mno$", "pqr$", "stu$", "vwxyz$".
Depending on the users first letter in their username a folder is created with the which is the users home area.
Example username - AdamB will be put in Networkshare1_abc$.
Example 2 username - EdwardB will be put in Networkshare1_def$
Within this folder are the is a list of all users as a folder
Networkshare1_abc$
Networkshare1_def$
Networkshare1_ghi$
Networkshare1_jkl$
Networkshare1_mno$
Networkshare1_pqr$
Networkshare1_stu$
Networkshare1_vwxy$
I need a script that will
that will loop though only the top folder list the users
check to see if a text file exists and if it does delete it (have this working below)
$Wantedfile = "Networkshare1_stu$\user1\test.txt"
$timeStamp = (Get-Date -Format "dd-MM-yyyy-hh-mm")
$FileName = $timeStamp + ".txt"
if ((Test-Path $Wantedfile$FileName) -eq $false) {
Write-Host "file does not exists"
} elseif ((Test-Path $Wantedfile$FileName) -eq $true) {
Write-Host "file present..removing file"
Remove-Item $Wantedfile$FileName
}
The bit I am struggling with is the loop and how to get it to check each folder in the above locations as streamline as possible.
I am not sure if your files are really named like
"Networkshare1_stu$\user1\test.txt03-21-2017-19-41.txt"
but basically what you need is foreach loop. You need to get all the folders in the top folder. In $directories you will have abc$, def$ etc. folders. Then you will loop through each of them and search for the file using Where-Object.
$nameOfFile = "test.txt"
$fullPathFile = "\\Networkshare1_abc$\user1\test.txt"
$directories = Get-ChildItem "\\Networkshare1" -Directory
foreach($directory in $directories)
{
Write-Host "Searching in $directory.FullName ..."
$files = Get-ChildItem $directory -Recurse | Where-Object{$_.FullName -like "*$nameOfFile"}
# or to use fullPathFile
# $files = Get-ChildItem $directory -Recurse | Where-Object{$_.FullName -eq $fullPathFile}
foreach($file in $files)
{
Write-Host "Removing $file.FullName - continue?"
Read-Host ""
Remove-Item $file -Force
}
}

Powershell script to check if all files from a list of files exist in given folder folder

I need to write a powershell script, input a list of file names and check a given folder. I need it to echo if all files from list are present in the folder( if not which are not) and the other way around - are all files in the folder listed in the inputted list of file names.
Im new to powershell, just finished my first script to rename all files in a folder and I dont have a clue how to input a list and iterate thgrough it while checking file names in the folder.
I've managed to write smomething like this:
$Dir2 = "C:\Users\Administrator\Desktop\testDir2"
$filenames = 'a.txt', 'b.txt', 'c.txt', 'd.txt'
foreach ($filename in $filenames) {
$found=$false;
Get-ChildItem -Path $Dir2 -Recurse | ForEach-Object {if($filename -eq $_.Name) {Write-Host $filename ' Ok' -foregroundcolor green; $found=$true;CONTINUE }$found=$false;} -END {if($found -ne $true){ Write-Host $filename ' missing' -foregroundcolor red}}
}
I still need to check the other way around + I need to somehow convert rows from excel sheet to the list of filenames
To retrieve the list from a text file, use the [Get-Content cmdlet]:
$FileList = Get-Content -Path .\myFileList.txt
To retrieve the files in the folder, use the Get-ChildItem cmdlet:
$Files = Get-ChildItem -Path C:\path\to\folder -File
Grab the file names with Select-Object:
$Files = $Files |Select-Object -Property Name
And finally compare the two lists with Compare-Object:
$Discrepancies = #(Compare-Object $FileList $Files)
If Compare-Object didn't return anything, there will have been no difference between the two lists:
if($Discrepancies.Count -eq 0)
{
Write-Host "Everything is as expected!"
}
Ok, I have the code that suits my needs:
(list of files is given in a file it can be csv)
$Dir2 = 'C:\Users\Administrator\Desktop\testDir2'
$filenames=Get-Content $Dir2\filenamesnoext.csv
foreach ($filename in $filenames) {
$found=$false;
Get-ChildItem -Path $Dir2 -Recurse | ForEach-Object {if($filename -eq $_.BaseName) {Write-Host 'FILE ' $filename ' Ok' -foregroundcolor green; $found=$true;CONTINUE }$found=$false;} -END {if($found -ne $true){ Write-Host 'FILE ' $filename ' missing in the folder' -foregroundcolor red}}
}
Get-ChildItem -Path $Dir2 -Recurse | ForEach-Object {$found=$false; foreach ($filename in $filenames) {if($filename -eq $_.BaseName) {Write-Host 'FILE ' $_.BaseName ' was found on the list' -foregroundcolor cyan; $found=$true;BREAK }} if($found -ne $true){ Write-Host 'FILE ' $_.BaseName ' missing on the list of files' -foregroundcolor Magenta} }
Here is another version if someone finds it more readable (no skipping loop iterations, getting file names only once):
$folder = 'D:\stuff'
$files = #(
"one.txt",
"two.txt"
)
Write-Host "Folder: $folder."
# Get only files and only their names
$folderFiles = Get-ChildItem -Path $folder -Recurse -File -Name
foreach ($f in $files) {
if ($folderFiles -contains $f) {
Write-Host "File $f was found." -foregroundcolor green
} else {
Write-Host "File $f was not found!" -foregroundcolor red
}
}

PowerShell: Set-Content having issues with "file already in use"

I'm working on a PowerShell script that finds all the files with PATTERN within a given DIRECTORY, prints out the relevant lines of the document with the PATTERN highlighted, and then replaces the PATTERN with a provided REPLACE word, then saves the file back. So it actually edits the file.
Except I can't get it to alter the file, because Windows complains about the file already being open. I tried several methods to solve this, but keep running into the issue. Perhaps someone can help:
param(
[string] $pattern = ""
,[string] $replace = ""
,[string] $directory ="."
,[switch] $recurse = $false
,[switch] $caseSensitive = $false)
if($pattern -eq $null -or $pattern -eq "")
{
Write-Error "Please provide a search pattern." ; return
}
if($directory -eq $null -or $directory -eq "")
{
Write-Error "Please provide a directory." ; return
}
if($replace -eq $null -or $replace -eq "")
{
Write-Error "Please provide a string to replace." ; return
}
$regexPattern = $pattern
if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern" }
$regex = New-Object System.Text.RegularExpressions.Regex $regexPattern
function Write-HostAndHighlightPattern([string] $inputText)
{
$index = 0
$length = $inputText.Length
while($index -lt $length)
{
$match = $regex.Match($inputText, $index)
if($match.Success -and $match.Length -gt 0)
{
Write-Host $inputText.SubString($index, $match.Index) -nonewline
Write-Host $match.Value.ToString() -ForegroundColor Red -nonewline
$index = $match.Index + $match.Length
}
else
{
Write-Host $inputText.SubString($index) -nonewline
$index = $inputText.Length
}
}
}
Get-ChildItem $directory -recurse:$recurse |
Select-String -caseSensitive:$caseSensitive -pattern:$pattern |
foreach {
$file = ($directory + $_.FileName)
Write-Host "$($_.FileName)($($_.LineNumber)): " -nonewline
Write-HostAndHighlightPattern $_.Line
%{ Set-Content $file ((Get-Content $file) -replace ([Regex]::Escape("[$pattern]")),"[$replace]")}
Write-Host "`n"
Write-Host "Processed: $($file)"
}
The issue is located within the final block of code, right at the Get-ChildItem call. Of course, some of the code in that block is now a bit mangled due to me trying to fix the problem then stopping, but keep in mind the intent of that part of the script. I want to get the content, replace the words, then save the altered text back to the file I got it from.
Any help at all would be greatly appreciated.
Removed my previous answer, replacing it with this:
Get-ChildItem $directory -recurse:$recurse
foreach {
$file = ($directory + $_.FileName)
(Get-Content $file) | Foreach-object {
$_ -replace ([Regex]::Escape("[$pattern]")),"[$replace]")
} | Set-Content $file
}
Note:
The parentheses around Get-Content to ensure the file is slurped in one go (and therefore closed).
The piping to subsequent commands rather than inlining.
Some of your commands have been removed to ensure it's a simple test.
Just a suggestion but you might try looking at the documentation for the parameters code block. There is a more efficient way to ensure that a parameter is entered if you require it and to throw an error message if the user doesn't.
About_throw: http://technet.microsoft.com/en-us/library/dd819510.aspx
About_functions_advanced_parameters: http://technet.microsoft.com/en-us/library/dd347600.aspx
And then about using Write-Host all the time: http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx
Alright, I finally sat down and just typed everything sequentially in PowerShell, then used that to make my script.
It was actually really simple;
$items = Get-ChildItem $directory -recurse:$recurse
$items |
foreach {
$file = $_.FullName
$content = get-content $file
$newContent = $content -replace $pattern, $replace
Set-Content $file $newcontent
}
Thanks for all your help guys.

File moving according to substring derived from file name

Ihave started a simple script in order to move files of a certain prefix to a folder of the same name eg: W100_11.jpg W100_12.jpg in to folder W100.
Thanks to help from the answers below I have progressed and have a successful loop which can iterate through the file sin the folder, I am having issues with the -filter switch and when trying to use the move-item cmdlet I am getting errors
The current code is:
$sourceDir = read-host "Please enter source Dir:"
$format = read-host "Format to look for with . :"
#$length = read-host "length of folder name:"
gci -Path $sourceDir | % {
If( -not $_.PSIsContainer)
{
$path = $sourceDir + "\" + $_.Name.substring(0, 3)
$_
if(-not (Test-Path $path))
{
mkdir $path
}
move $_.fullname $path
}
}
I am still having issues when using the -filter switch. This is a partial solution to the issue
The code below shortens your for loop and shows an example of using substrings on file names. You get a FileInfo object from the Get-ChildItem (gci) call, so you need to use it's property Name to do substrings. See MSDN for more info on FileInfo.
$sourceDir = read-host Please enter source Dir
$format = read-host Format to look for with .
gci -Path $sourceDir -filter $format | % {
If( -not $_.PSIsContainer)
{
$path = $sourceDir + "\" + $_.Name.substring(0, 3)
if(-not (Test-Path $path))
{
mkdir $path
}
move $_ $path
}
}
If files names are always with this prefix "W100" you can easily take a substring in this way:
$a = "w100_12.ps1"
$a.Substring(0,4) # give W100
if prefix is not fix give more sample to help you solve it.
When piping use the built-in variable $_ to refer to the piped object, the '%' sign is an alias for 'for each':
get-childitem -Path $sourceDir -Filter $format -Recurse | % {If ($_.extension -eq ".ps1"){Write-Host $_.fullname}}
Answers above refer to substring, here is the MS Technet page that explains Powershell substring usage:
http://technet.microsoft.com/en-us/library/ee692804.aspx

Resources