Powershell Scripting Variables - powershell

I have set some variables in PowerShell. The variables are created at the beginning of my script. However, the values for the variables are being executed at start which in turns gives and error message. Ex:
$checker = get-item -path C:\users\user\desktop\Foldername
$finder = Test-path -Path $checker
if($finder -eq $finder )
{
}
Else
{
Create-Item -Path C:/users/user/desktop -name "Foldername" -itemtype Directory
}
I do know that if I run this it will give me an error because the directory never existed and I can just change the variable order to avoid errors.
My question is that this script is going to be more lines of code than this and I would have to create the variable right when its needed to avoid errors.
How can I use these variables like a regular programming language where the variables are ignored until called upon.

Using Get-Item and checking with Test-Path afterwards is not a good design. Two better ways:
Use Get-Item only and check for $null to check for its existence:
$checker = Get-Item -Path C:\users\user\desktop\Foldername -ErrorAction SilentlyContinue
if ($checker) {
# do something with the existing folder
} else {
Create-Item -Path C:/users/user/desktop -Name "Foldername" -ItemType Directory
}
Use Test-Path only to check for its existence:
if (Test-Path -Path C:\users\user\desktop\Foldername) {
# do something with the existing folder
} else {
Create-Item -Path C:/users/user/desktop -Name "Foldername" -ItemType Directory
}

Related

Powershell - Get path location via Find-Path Function

So i wrote the code below
$datum_vandaag = $(Get-Date).toString('yyyy-MM-dd')
$maand = $datum_vandaag.substring(5, 2)
if (-Not (Test-Path -Path "C:\Users\Nick\Desktop\ITIL\**" -Include *$maand*)) { #(contains words gelijk aan (get-date)
md -Path "C:\Users\Nick\Desktop\ITIL\[??????]\$datum_vandaag" # Makes folder with name of current date in path:
}
Else{Write-Output "test output"
}
The IF function is looking if in the path Desktop\ITIL** there is a folder that has the same number as the current date (07 is july) somewhere in the name of the folder.
Now i would like to make a new folder in the folder that is found by the command below :
Test-Path -Path "C:\Users\Nick\Desktop\ITIL\**" -Include *$maand*
How could i get acces to this path so i can use it in the md command (now marked with [??????] in the code) Because currently i only receive if a folder is found, false or true.
All i could find what possibly would work is get-path or resolve-path but I don't know how to implement this.
It's difficult to fully understand what you are asking, but I think the command you are looking for is Get-Item which will return an object to the named folder.
Something like this might work:
$path = Get-Item -Path "C:\Users\Nick\Desktop\ITIL\**" -Include *$maand*
if ($path) {
$newPath = Join-Path -Path $path -ChildPath $datum_vandaag
New-Item -Path $newPath -ItemType Directory
}

Passing down a variable to function creates an array

I have already asked about Powershell return values in Powershell, but I can simply no wrap my head around why the following New-FolderFromName is returning an array - I expected one value(a path or a string) in return:
$ProjectName="TestProject"
function New-FolderFromPath($FolderPath){
if($FolderPath){
if (!(Test-Path -Path $FolderPath)) {
Write-Host "creating a new folder $FolderName..." -ForegroundColor Green
New-Item -Path $FolderPath -ItemType Directory
}else{
Write-Host "Folder $FolderName already exist..." -ForegroundColor Red
}
}
}
function New-FolderFromName($FolderName){
if($FolderName){
$CurrentFolder=Get-Location
$NewFolder=Join-Path $CurrentFolder -ChildPath $FolderName
New-FolderFromPath($NewFolder)
return $NewFolder
}
}
$ProjectPath=New-FolderFromName($ProjectName)
Write-Host $ProjectPath
Also tried to add the following to New-FolderFromPath as this function seems to be the problem or altering the parameter:
[OutputType([string])]
param(
[string]$FolderPath
)
This happens as a Powershell function will return everything on the pipeline instead of what's just specified with return.
Consider
function New-FolderFromName($FolderName){
if($FolderName){
$CurrentFolder=Get-Location
$NewFolder=Join-Path $CurrentFolder -ChildPath $FolderName
$ret = New-FolderFromPath($NewFolder)
write-host "`$ret: $ret"
return $NewFolder
}
}
#output
PS C:\temp> New-FolderFromName 'foobar'
creating a new folder foobar...
$ret: C:\temp\foobar
C:\temp\foobar
See, the New-FolderFromPath returned a value which originates from New-Item. The easiest way to get rid of extra return values is to pipe New-Item to null like so,
New-Item -Path $FolderPath -ItemType Directory |out-null
See also another a question about the behaviour.

New-Item executed even though it's within an 'if' statement

I created a script to pull JPEG snapshots from 2 IP cameras. For me to keep them organized I added some lines to check the date and create a folder matching it. The script also checks if the folder exists and if it does, should skip to the snapshot capture.
Everything works fine as intended but it seems for one reason or another the script still tries to create the folder and shows and error in my PS console that the directory exists.
$chk_path = Test-Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$Camera_A = (new-object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))\Camera_A$((Get-Date).ToString('HH-mm-ss')).jpeg")
$Camera_B = (new-object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))\Camera_B$((Get-Date).ToString('HH-mm-ss')).jpeg")
if (-not ($chk_path) ) {
write-host "C:\SnapShots doesn't exist, creating it"
$Make_ScrapShot_Folder
} else {
write-host "C:\SnapShots exists, Saving SnapShots"
}
Camera_A
Camera_B
Unlike other languages probably you've used, PowerShell will execute the code when you assign it to a variable not when the variable is called.
So you're actually creating the folder with this line:
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
You just can re-arrange your code to make it do what you want, and to be more efficient:
$day = Get-Date -Format 'yyyy-MM-dd'
$snapshots_dir = "C:\SnapShots\$day"
if (Test-Path $snapshots_dir) {
Write-Host "$snapshots_dir exists, Saving SnapShots"
} else {
Write-Host "$snapshots_dir doesn't exist, creating it"
New-Item -ItemType Directory -Path $snapshots_dir
}
$timestamp = Get-Date -Format 'HH-mm-ss'
(New-Object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"$snapshots_dir\Camera_A$timestamp.jpeg")
(New-Object System.Net.WebClient).DownloadFile('http://10.0.0.132/snap.jpeg',"$snapshots_dir\Camera_B$timestamp.jpeg")
The following code snippet should work:
$str_path = "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
$chk_path = Test-Path $str_path
if ($chk_path) {
$Make_SnapShot_Folder = Get-Item -Path $str_path
} else {
$Make_SnapShot_Folder = New-Item -ItemType Directory -Path $str_path
}
$Make_SnapShot_Folder ### this outputs a DirectoryInfo
Move this line:
$Make_SnapShot_Folder =
New-Item -ItemType Directory -Path "C:\SnapShots\$((Get-Date).ToString('yyyy-MM-dd'))"
To here:
if (-not ($chk_path) ) {
<-- Here
write-host "C:\SnapShots doesn't exist, creating it"
$Make_ScrapShot_Folder <-- Remove this line

Perform file verification check with Copy-Item

The following script creates a folder named the specified date on the servers in servers.txt, then copies the folder from which the script is run to that folder.
For example, it creates the folder "3-22-15 (night)" on SERVER1, SERVER2, etc., then copies "Directory Containing the Script" to "3-22-15 (night)".
$CurrentLocation = get-location
$DeploymentDate = "3-22-15 (night)"
Foreach($server in get-content "servers.txt"){
New-Item -ErrorAction SilentlyContinue -ItemType directory -Path \\$server\C$\Deployments\$DeploymentDate
copy-item -Path $CurrentLocation -Destination \\$server\C$\Deployments\$DeploymentDate\ -ErrorAction SilentlyContinue -recurse
}
How do I modify the script to include file verification for each file that is copied to \\$server\C$\Deployments\$DeploymentDate\?
I would like it to output an error message with the file that does not pass the verification check, but to continue copying.
I was going to try something like this:
function SafeCopy ($SourcePath,$DestinationPath,$SourceFileName) {
#MD5 Check Function
function Check-MD5 ($FilePath) {
$md5=New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash=[System.BitConverter]::ToString($md5.ComputeHash([System.IO.File]::ReadAllBytes($FilePath)))
Return $hash
} # EndOf function Check-MD5
$MD5Source=Check-MD5 $SourcePath
Copy-Item $SourcePath\$SourceFileName $DestinationPath
$MD5Destination=Check-MD5 $DestinationPath
if (Test-Path $DestinationPath) {
if ($MD5Destination -match $MD5Source) {
Write-Host "`nThe file `"$SourcePath`" has been copied in `"$DestinationPath`" successfully.`n" -ForegroundColor DarkGreen
} else {
Write-Host "`nThe file `"$SourcePath`" has been copied in `"$DestinationPath`" but the CRC check failed!`n" -ForegroundColor DarkRed
}
} else {
Write-Host "`nThe file `"$SourcePath`" has not been copied in `"$DestinationPath`"!`n" -ForegroundColor DarkRed
}
} # EndOf function SafeCopy
But I'm not sure how to implement it.
I am not a talented scripter nor do I play one on the radio but I've put this together and it seems to work. As mentioned, the larger the filesize being compared, the slower it goes.
Param
(
[parameter(Mandatory=$true,Position=1,ValueFromPipeLine=$true)][string]$source,
[parameter(Mandatory=$true,Position=2,ValueFromPipeLine=$true)][string]$dest
)
$countsource = #(Get-ChildItem -Path $source -Directory)
$countdest = #(Get-ChildItem -Path $dest -Directory)
IF($countsource = $countdest){
"$source equals $dest"
}
Else{
"$source does not match $dest"
}
Sample usage and output. TODO: Add folder item count compare, warn or break if the folders do not have the same number of items (subfolders and files).
So copy the folder then run Folder-Compare as a function once the copy is finished.
PS C:\Scripts> .\Folder-Compare.ps1 -source C:\Scripts\temp -dest C:\aaa
C:\Scripts\temp does not match C:\aaa

Create directory if it does not exist

I am writing a PowerShell script to create several directories if they do not exist.
The filesystem looks similar to this
D:\
D:\TopDirec\SubDirec\Project1\Revision1\Reports\
D:\TopDirec\SubDirec\Project2\Revision1\
D:\TopDirec\SubDirec\Project3\Revision1\
Each project folder has multiple revisions.
Each revision folder needs a Reports folder.
Some of the "revisions" folders already contain a Reports folder; however, most do not.
I need to write a script that runs daily to create these folders for each directory.
I am able to write the script to create a folder, but creating several folders is problematic.
Try the -Force parameter:
New-Item -ItemType Directory -Force -Path C:\Path\That\May\Or\May\Not\Exist
You can use Test-Path -PathType Container to check first.
See the New-Item MSDN help article for more details.
$path = "C:\temp\NewFolder"
If(!(test-path -PathType container $path))
{
New-Item -ItemType Directory -Path $path
}
Test-Path -PathType container checks to see if the path exists and is a directory. When it does not, it will create a new directory. If the path exists but is a file, New-Item will raise an error (you can overwrite the file by using the -force argument if you are risky).
[System.IO.Directory]::CreateDirectory('full path to directory')
This internally checks for directory existence, and creates one, if there is no directory. Just one line and native .NET method working perfectly.
Use:
$path = "C:\temp\"
If (!(test-path $path))
{
md $path
}
The first line creates a variable named $path and assigns it the string value of "C:\temp"
The second line is an If statement which relies on the Test-Path cmdlet to check if the variable $path does not exist. The not exists is qualified using the ! symbol.
Third line: If the path stored in the string above is not found, the code between the curly brackets will be run.
md is the short version of typing out: New-Item -ItemType Directory -Path $path
Note: I have not tested using the -Force parameter with the below to see if there is undesirable behavior if the path already exists.
New-Item -ItemType Directory -Path $path
The following code snippet helps you to create a complete path.
Function GenerateFolder($path) {
$global:foldPath = $null
foreach($foldername in $path.split("\")) {
$global:foldPath += ($foldername+"\")
if (!(Test-Path $global:foldPath)){
New-Item -ItemType Directory -Path $global:foldPath
# Write-Host "$global:foldPath Folder Created Successfully"
}
}
}
The above function split the path you passed to the function and will check each folder whether it exists or not. If it does not exist it will create the respective folder until the target/final folder created.
To call the function, use below statement:
GenerateFolder "H:\Desktop\Nithesh\SrcFolder"
I had the exact same problem. You can use something like this:
$local = Get-Location;
$final_local = "C:\Processing";
if(!$local.Equals("C:\"))
{
cd "C:\";
if((Test-Path $final_local) -eq 0)
{
mkdir $final_local;
cd $final_local;
liga;
}
## If path already exists
## DB Connect
elseif ((Test-Path $final_local) -eq 1)
{
cd $final_local;
echo $final_local;
liga; (function created by you TODO something)
}
}
When you specify the -Force flag, PowerShell will not complain if the folder already exists.
One-liner:
Get-ChildItem D:\TopDirec\SubDirec\Project* | `
%{ Get-ChildItem $_.FullName -Filter Revision* } | `
%{ New-Item -ItemType Directory -Force -Path (Join-Path $_.FullName "Reports") }
BTW, for scheduling the task please check out this link: Scheduling Background Jobs.
There are three ways I know to create a directory using PowerShell:
Method 1: PS C:\> New-Item -ItemType Directory -path "C:\livingston"
Method 2: PS C:\> [system.io.directory]::CreateDirectory("C:\livingston")
Method 3: PS C:\> md "C:\livingston"
From your situation it sounds like you need to create a "Revision#" folder once a day with a "Reports" folder in there. If that's the case, you just need to know what the next revision number is. Write a function that gets the next revision number, Get-NextRevisionNumber. Or you could do something like this:
foreach($Project in (Get-ChildItem "D:\TopDirec" -Directory)){
# Select all the Revision folders from the project folder.
$Revisions = Get-ChildItem "$($Project.Fullname)\Revision*" -Directory
# The next revision number is just going to be one more than the highest number.
# You need to cast the string in the first pipeline to an int so Sort-Object works.
# If you sort it descending the first number will be the biggest so you select that one.
# Once you have the highest revision number you just add one to it.
$NextRevision = ($Revisions.Name | Foreach-Object {[int]$_.Replace('Revision','')} | Sort-Object -Descending | Select-Object -First 1)+1
# Now in this we kill two birds with one stone.
# It will create the "Reports" folder but it also creates "Revision#" folder too.
New-Item -Path "$($Project.Fullname)\Revision$NextRevision\Reports" -Type Directory
# Move on to the next project folder.
# This untested example loop requires PowerShell version 3.0.
}
PowerShell 3.0 installation.
Here's a simple one that worked for me. It checks whether the path exists, and if it doesn't, it will create not only the root path, but all sub-directories also:
$rptpath = "C:\temp\reports\exchange"
if (!(test-path -path $rptpath)) {new-item -path $rptpath -itemtype directory}
I wanted to be able to easily let users create a default profile for PowerShell to override some settings, and ended up with the following one-liner (multiple statements yes, but can be pasted into PowerShell and executed at once, which was the main goal):
cls; [string]$filePath = $profile; [string]$fileContents = '<our standard settings>'; if(!(Test-Path $filePath)){md -Force ([System.IO.Path]::GetDirectoryName($filePath)) | Out-Null; $fileContents | sc $filePath; Write-Host 'File created!'; } else { Write-Warning 'File already exists!' };
For readability, here's how I would do it in a .ps1 file instead:
cls; # Clear console to better notice the results
[string]$filePath = $profile; # Declared as string, to allow the use of texts without plings and still not fail.
[string]$fileContents = '<our standard settings>'; # Statements can now be written on individual lines, instead of semicolon separated.
if(!(Test-Path $filePath)) {
New-Item -Force ([System.IO.Path]::GetDirectoryName($filePath)) | Out-Null; # Ignore output of creating directory
$fileContents | Set-Content $filePath; # Creates a new file with the input
Write-Host 'File created!';
}
else {
Write-Warning "File already exists! To remove the file, run the command: Remove-Item $filePath";
};
$mWarningColor = 'Red'
<#
.SYNOPSIS
Creates a new directory.
.DESCRIPTION
Creates a new directory. If the directory already exists, the directory will
not be overwritten. Instead a warning message that the directory already
exists will be output.
.OUTPUT
If the directory already exists, the directory will not be overwritten.
Instead a warning message that the directory already exists will be output.
.EXAMPLE
Sal-New-Directory -DirectoryPath '.\output'
#>
function Sal-New-Directory {
param(
[parameter(mandatory=$true)]
[String]
$DirectoryPath
)
$ErrorActionPreference = "Stop"
try {
if (!(Test-Path -Path $DirectoryPath -PathType Container)) {
# Sal-New-Directory is not designed to take multiple
# directories. However, we use foreach to supress the native output
# and substitute with a custom message.
New-Item -Path $DirectoryPath -ItemType Container | `
foreach {'Created ' + $_.FullName}
} else {
Write-Host "$DirectoryPath already exists and" `
"so will not be (re)created." `
-ForegroundColor $mWarningColor
}
} finally {
$ErrorActionPreference = "Continue"
}
}
"Sal" is just an arbitrary prefix for my own library. You could remove it or replace it with your own.
Another example (place here because it otherwise ruins stackoverflow syntax highlighting):
Sal-New-Directory -DirectoryPath ($mCARootDir + "private\")
Example, create a 'Reports' folder inside of the script's folder.
$ReportsDir = $PSScriptRoot + '\Reports'
$CreateReportsDir = [System.IO.Directory]::CreateDirectory($ReportsDir)