I had a script that was functioning properly before I began breaking it into smaller parts. However, I’ve noticed that as I try to use variables, the information is not being brought in. I have to manually copy and paste the variable into my “if”, “else”, “while”, or other functions for them to work. I was advised to use “$script:”, but from what I understand, that only allows the variable to be “reported out” of the function for later use. I don’t need that, I just need to pull the information in.
My script so far:
- A user drops a folder into a designated drop-off folder.
- The script detects a new file and starts running.
- The name of the folder is recorded.
- All files are moved from their original folder structure to another folder.
- These files are then converted into a single .pdf.
- The .pdf is moved to a “completed” folder.
- All empty folders and files are deleted.
- The script runs all these actions upon startup, and then waits for updates.
My code:
#File Locations
$rootPath = 'C:\IT\'
$inLoc = 'Convert Drop'
$prossLoc = 'Processing'
$outLoc = 'Converted PDF'
#File types to include in PDF creation.
$fileTypes = '*.{png,jpeg,jpg,tiff,tif}'
#Function Variables
$inPath = Join-Path -Path "$rootPath" -ChildPath "$inLoc"
$outPath = Join-Path -Path "$rootPath" -ChildPath "$outLoc"
$runPath = Join-Path -Path "$rootPath" -ChildPath "$prossLoc"
$remove1 = Join-Path -Path "$rootPath" -ChildPath "$($inLoc + "\*")"
$remove2 = Join-Path -Path "$rootPath" -ChildPath "$($outLoc + "\*")"
#Folder Watching Variables
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "$inPath"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
#Lone Counter
$freshStart = $null
$statusOld = $null
$pathLoc = (Get-Item -Path ".\").FullName
#Pulls the last write time of a folder to compare later.
$grabStatus = {$status = Get-Item $pathLoc | Foreach { $_.LastWriteTime } }
#Get PDF name from Folder
$grabFileName = {
$folder = get-childitem -Path $inPath -Directory -Name
$fileName = $folder + ".pdf"
}
#Move all nested files to single folder.
$moveFiles = {
Get-ChildItem -Path $inPath -Recurse -File | Move-Item -Destination $runPath
}
#Convert Nested files into single PDF
$makePDF = {
& CD $runPath
& magick "$fileTypes" $fileName
}
#Move final PDF
$moveCmplt = {
Get-ChildItem -Path $pdf -File | Move-Item -Destination $outPath
}
#Delete Old files
$deleteOld = {
Remove-Item $remove1 -Recurse -Force
Remove-Item $remove2 -Recurse -Force
}
#Set compare status to current status then fetches new status.
$stats = {
$statusOld = $status
$grabStatus
sleep 10
}
#Exicute main conversion together.
$action = {
$grabStatus
If ($status -eq $statusOld){
$grabFileName
$moveFiles
& CD $runPath
$grabStatus
If ($status -eq $statusOld) {
$makePDF
}
Else{
$stats
}
$deleteOld
}
Else
{
$stats
}
}
#First Time Start, Then Loop run.
While ($freshStart -eq $null) {
If ((Get-ChildItem $inPath | Measure-Object).Count -eq 0) {
}
Else {
$action
}
$freshStart = "FreshStartDone!"
}
#Scan folder every 5 seconds for new content then run convert on change.
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}
UPDATED CODE It works on the first time run, but the loop is broken after converting everything to functions.
#File Locations
$rootPath = 'C:\IT\'
$inLoc = 'Convert Drop'
$prossLoc = 'Processing'
$outLoc = 'Converted PDF'
#File types to include in PDF creation.
$fileTypes = '*.{png,jpeg,jpg,tiff,tif}'
#Function Variables
$inPath = Join-Path -Path "$rootPath" -ChildPath "$inLoc"
$outPath = Join-Path -Path "$rootPath" -ChildPath "$outLoc"
$runPath = Join-Path -Path "$rootPath" -ChildPath "$prossLoc"
$remove1 = Join-Path -Path "$rootPath" -ChildPath "$($inLoc + "\*")"
$remove2 = Join-Path -Path "$rootPath" -ChildPath "$($prossLoc + "\*")"
#Folder Watching Variables
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "$inPath"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
#Lone Vars
$freshStart = $null
$statusOld = $null
$pathLoc = (Get-Item -Path ".\").FullName
#$pathMagick = 'C:\Program Files\ImageMagick-7.0.8-Q16\magick.exe'
#Pulls the last write time of a folder to compare later.
function grabStatus
{
& CD $runPath
$status = Get-Item $pathLoc | Foreach { $_.LastWriteTime }
}
#Get PDF name from Folder
function grabFileName
{
$folder = get-childitem -Path $inPath -Directory -Name
$global:fileName = $folder + ".pdf"
}
#Move all nested files to single folder.
function moveFiles
{
Get-ChildItem -Path $inPath -Recurse -File | Move-Item -Destination $runPath
}
#Convert Nested files into single PDF
function makePDF
{
& CD $runPath
& magick $fileTypes $global:fileName
}
#Move final PDF
function moveCmplt
{
Get-ChildItem -Path "$runPath\*.pdf" -File | Move-Item -Destination $outPath
}
#Delete Old files
function deleteOld
{
Remove-Item $remove1 -Recurse -Force
Remove-Item $remove2 -Recurse -Force
}
#Set compare status to current status then fetches new status.
function stats
{
$statusOld = $status
$grabStatus
sleep 10
}
#Exicute main conversion together.
function action
{
grabStatus
If ($status -eq $statusOld)
{
grabFileName
moveFiles
grabStatus
If ($status -eq $statusOld)
{
makePDF
grabStatus
If ($status -eq $statusOld)
{
grabStatus
moveCmplt
If ($status -eq $statusOld)
{
deleteOld
}
}
Else { stats }
}
Else { stats }
}
Else { stats }
}
$runIt = { action }
#First Time Start, Then Loop run.
While ($freshStart -eq $null)
{
If ((Get-ChildItem $inPath | Measure-Object).Count -eq 0)
{
}
Else
{
action
}
$freshStart = "FreshStartDone!"
}
#Scan folder every 5 seconds for new content then run convert on change.
Register-ObjectEvent $watcher "Created" -Action $runIt
#Register-ObjectEvent $watcher "Created" -Action $action
while ($true) { sleep 5 }
3 Answers
Introduction
In PowerShell, variables are used to store data that can be used later in the script. However, sometimes, when using variables in functions or loops, the information is not being sent into the function or loop. This can be a frustrating issue because it can prevent the script from working correctly. In this blog post, we will discuss why this issue occurs and how to solve it.
Understanding the Issue
The issue occurs when variables are not being passed into functions or loops. The reason for this is that the variables are not being defined in the correct scope. In PowerShell, there are three scopes: global, script, and local. The global scope is available to all sessions and scripts, the script scope is available to the current script, and the local scope is available only within the current function or loop.
When a variable is defined in the script scope, it is not automatically available in the function or loop scope. This means that the variable needs to be passed into the function or loop explicitly. This can be done by using the parameter block in the function or loop definition. However, this can be cumbersome and may not be practical for all situations.
Another issue is that the variables defined in the script scope can be overwritten by variables with the same name in the function or loop scope. This can cause unexpected behavior and can be difficult to debug.
Solving the Issue
To solve the issue, we need to define the variables in the correct scope. In this case, we need to define the variables in the script scope so that they are available to all functions and loops in the script. To do this, we can use the $script: prefix before the variable name.
The $script: prefix tells PowerShell to use the script scope instead of the local scope. This means that the variable is available to all functions and loops in the script. Here’s an example:
$script:rootPath = 'C:IT'
$script:inLoc = 'Convert Drop'
$script:prossLoc = 'Processing'
$script:outLoc = 'Converted PDF'
By using the $script: prefix, we can define the variables in the script scope and make them available to all functions and loops in the script.
Using Variables in Functions and Loops
Once we have defined the variables in the script scope, we can use them in functions and loops. To use the variables in a function or loop, we can pass them as parameters or use the $script: prefix.
Passing Variables as Parameters
Passing variables as parameters is a common way to use variables in functions and loops. To pass a variable as a parameter, we need to define the parameter in the function or loop definition. Here’s an example:
function Do-Something {
param (
[string]$Path
)
# Do something with $Path
}
In this example, we define a function called Do-Something that takes a parameter called $Path. We can then use the $Path variable in the function.
To call the function and pass the variable, we can do the following:
$Path = 'C:Temp'
Do-Something -Path $Path
In this example, we define a variable called $Path and set its value to ‘C:Temp’. We then call the Do-Something function and pass the $Path variable as a parameter.
Using the $script: Prefix
Another way to use variables in functions and loops is to use the $script: prefix. By using the $script: prefix, we can access the variable in the script scope from within the function or loop. Here’s an example:
$script:Path = 'C:Temp'
function Do-Something {
# Use $script:Path in the function
}
In this example, we define a variable called $script:Path and set its value to ‘C:Temp’. We then define a function called Do-Something that uses the $script:Path variable.
By using the $script: prefix, we can access the $script:Path variable from within the Do-Something function.
Conclusion
In conclusion, when variables are not being sent into functions or loops, the issue is caused by the variables not being defined in the correct scope. To solve this issue, we need to define the variables in the script scope using the $script: prefix. Once we have defined the variables in the script scope, we can use them in functions and loops by passing them as parameters or using the $script: prefix.
Based on the code you’ve provided, it seems that you are correctly passing the variables as arguments and they are defined before they are used. The problem seems to be that you are using the variables inside the script block passed to the Register-ObjectEvent and they are not in the scope of the script block.
You can fix this by passing the variables to the script block as arguments. In this way, the variables will be in the scope of the script block and can be used correctly. Here is an example of how you can pass the variables as arguments:
Register-ObjectEvent $watcher "Created" -Action {
Param($inPath, $outPath, $runPath, $remove1, $remove2, $fileName, $grabStatus, $stats, $grabFileName, $moveFiles, $makePDF, $moveCmplt, $deleteOld)
$grabStatus
If ($status -eq $statusOld){
$grabFileName
$moveFiles
& CD $runPath
$grabStatus
If ($status -eq $statusOld) {
$makePDF
}
Else{
$stats
}
$deleteOld
}
Else
{
$stats
}
} -ArgumentList $inPath, $outPath, $runPath, $remove1, $remove2, $fileName, $grabStatus, $stats, $grabFileName, $moveFiles, $makePDF, $moveCmplt, $deleteOld
Please keep in mind that this is an example and it might need further adjustment.
To summarize the solution:
I converted my script blocks to functions, for example:
$variable = {code}
Became:
function variable {code}
This worked for most of my script, but it had the unintended side effect of breaking my loop. The solution for that was to convert the main function I was using to a global function, for example:
function global:functionName {code}
After this, everything is working as it should. I would like to thank Pimp Juice IT, LPChip, and HackSlash for their assistance.
Working Script:
#File Locations
$rootPath = 'C:\IT\'
$inLoc = 'Convert_Drop'
$prossLoc = 'Processing'
$outLoc = 'Converted PDF'
#File types to include in PDF creation.
$fileTypes = '*.{png,jpeg,jpg,tiff,tif}'
#Function Variables
$inPath = Join-Path -Path "$rootPath" -ChildPath "$inLoc"
$outPath = Join-Path -Path "$rootPath" -ChildPath "$outLoc"
$runPath = Join-Path -Path "$rootPath" -ChildPath "$prossLoc"
$remove1 = Join-Path -Path "$rootPath" -ChildPath "$($inLoc + "\*")"
$remove2 = Join-Path -Path "$rootPath" -ChildPath "$($prossLoc + "\*")"
#Folder Watching Variables
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "$inPath"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
#Lone Vars
$freshStart = $null
$statusOld = $null
$pathLoc = (Get-Item -Path ".\").FullName
#Pulls the last write time of a folder to compare later.
function grabStatus
{
& CD $runPath
$status = Get-Item $pathLoc | Foreach { $_.LastWriteTime }
}
#Get PDF name from Folder
function grabFileName
{
$folder = get-childitem -Path $inPath -Directory -Name
$global:fileName = $folder + ".pdf"
}
#Move all nested files to single folder.
function moveFiles
{
Get-ChildItem -Path $inPath -Recurse -File | Move-Item -Destination $runPath
}
#Convert Nested files into single PDF
function makePDF
{
& CD $runPath
& magick $fileTypes $global:fileName
}
#Move final PDF
function moveCmplt
{
Get-ChildItem -Path "$runPath\*.pdf" -File | Move-Item -Destination $outPath
}
#Delete Old files
function deleteOld
{
Remove-Item $remove1 -Recurse -Force
Remove-Item $remove2 -Recurse -Force
}
#Set compare status to current status then fetches new status.
function stats
{
$statusOld = $status
$grabStatus
sleep 10
}
#Exicute main conversion together.
function global:runIt
{
grabStatus
If ($status -eq $statusOld)
{
grabFileName
moveFiles
grabStatus
If ($status -eq $statusOld)
{
makePDF
grabStatus
If ($status -eq $statusOld)
{
grabStatus
moveCmplt
If ($status -eq $statusOld)
{
deleteOld
}
}
Else { stats }
}
Else { stats }
}
Else { stats }
}
#$runIt = { action }
#First Time Start, Then Loop run.
While ($freshStart -eq $null)
{
If ((Get-ChildItem $inPath | Measure-Object).Count -eq 0)
{
}
Else
{
global:runIt
}
$freshStart = "FreshStartDone!"
}
#Scan folder every 5 seconds for new content then run convert on change.
Register-ObjectEvent $watcher "Created" -Action { global:runIt }
while ($true) { sleep 5 }