Link to home
Start Free TrialLog in
Avatar of Ron Shorts
Ron ShortsFlag for United States of America

asked on

Powershell read file name into variable and number

I have the below powershell script that I'm reading a file, then creating new files and appending a +1 on the end of.  At the beginning of the script, I'm clearing all of the files in the parent directory with a wildcard - but I don't want to do this.  I have another routine that will cleanup the files as they are cycled through.

Basically I'm trying to create a file, starting with comptuerlist1, but only if comptuerlist1 doesn't exist, then the next available number.

Any help is appreciated.


# Split inputlist.txt into blocks of 5
$linecount = 0 
$filenumber = 1 
# 13 over +1 into variable...
$sourcefilename = "c:\script\source\computerlist.txt"
$destinationfolderpath = "c:\Script\"
$destinationfilesize = "5"
$maxsize = [int]$destinationfilesize  

Remove-Item -path $destinationfolderpath\computerlist*  # remove, put in win10qa without wildcard.

Get-Content $sourcefilename | Measure-Object | ForEach-Object { $sourcelinecount = $_.Count } 

$content = get-content $sourcefilename | % { 
 Add-Content $destinationfolderpath\computerlist$filenumber.txt "$_" 
  $linecount ++ 
  If ($linecount -eq $maxsize) { 
    $filenumber++ 
    $linecount = 0 
  } 
} 
 
Clear-Content $sourcefilename

Open in new window

Avatar of FDiskWizard
FDiskWizard
Flag of United States of America image

I think you want TEST-PATH.

if ( -not (Test-Path $sourcefilename) ) {  

 #Process file

 }
Avatar of oBdA
oBdA

Something like this?
# Split inputlist.txt into blocks of 5
$fileIndexStart = 1
$fileIndexLength = 3
$fileNameTemplate = 'computerlist{0}.txt'
# 13 over +1 into variable...
$sourceFile = "C:\script\source\computerlist.txt"
$destinationFolder = "C:\Script"
$destinationFilesize = 5

# Remove-Item -Path "$($destinationFolder)\$($fileNameTemplate -f '*')"  # remove, put in win10qa without wildcard.

$sourcelinecount = Get-Content -Path $sourceFile | Measure-Object | Select-Object -ExpandProperty Count
$linecount = 0
$currentFileIndex = $fileIndexStart
$destinationFile = $null
Get-Content -Path $sourceFile | ForEach-Object {
	If ((-not $destinationFile) -or ($linecount -eq $destinationFilesize)) {
		$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
		While (Test-Path -Path $destinationFile) {
			$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
		}
		Write-Host "Creating new file '$([IO.Path]::GetFileName($destinationFile))'"
		$linecount = 0
	}
	Add-Content -Path $destinationFile -Value $_
	$linecount++
}

Clear-Content -Path $sourceFile

Open in new window

Avatar of Ron Shorts

ASKER

oBdA - this is it exactly - thank you!!  I'm trying to tie this into my scripts to make this name somewhat dynamic - right now, I'm hard coding each input file (comuterlistxxx.txt into separate scripts and executing them in a wrapper.


If you can help, here is what I have, I very much appreciate your help!  


1.  I'm using the script you just updated to prepare the directory with input files I'm running against.  I have been hard coding each input file to match each script (script1.ps1) below matches computerlist001.txt for example.  (this is because I'm using workflow, with parallel processing and inline block which is a limit of 5)

2.  After the directory and input files are prepared, I'm running one script per input file in a wrapper.ps1 below.


I would like to tie in somehow - not sure if this would be in the wrapper, or the script1.ps1 the newly generated computerlistxxx.txt into a variable I can use per each script, then delete after completed processing. 


# Wrapper.ps1
$Dest = "c:\script"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script1-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script2-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script3-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script4-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script5-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script6-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script7-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script8-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script9-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script10-ps1"

Open in new window

# script1.ps1
$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
         $n = 5
         $m = 1 
function Test-MyConnection {
    param (
    [Parameter(Mandatory=$true)]
    [System.String]
    $ComputerName
    )
   
    Test-Connection $computername -Quiet -Count 2
}  
# Misc $variables here
# Misc Functions here
# Misc Functions here
# Misc Functions here
# Misc Functions here
# function call block using iterations
        For ($i=1; $i -le $n; $i++) {
                 
              foreach ( $computer in $using:computer )
              {
               If ( Test-MyConnection -ComputerName $computer )
               {
                    #Function calls here
                    #Function calls here
                    #Function calls here
                    #Function calls here
                    #Function calls here
               }
                Else
               {
                  Write-Output "$($using:computer) is offline, $i, check power state.  Logged in Offline.log"
                  write-output "$using:computer Offline, $Today, $i, $Runninguser" | Out-File $OfflineLog -Append -ErrorAction SilentlyContinue
               }
            }        
         }
       }
    }
} 
$ComputerList  = Get-Content "C:\Script\computerlist001.txt"
foreachrerun -Computers $ComputerList

Open in new window


Use a single worker script, and pass the file to process as argument.
This is the wrapper; it collects the files it created and then starts a new process for each one.
# Absolute path to the worker script:
$workScript = "C:\script\source\Script.ps1"
# Split inputlist.txt into blocks of 5
$fileIndexStart = 1
$fileIndexLength = 3
$fileNameTemplate = 'computerlist{0}.txt'
# 13 over +1 into variable...
$sourceFile = "C:\script\source\computerlist.txt"
$destinationFolder = "C:\Script"
$destinationFilesize = 5

# Remove-Item -Path "$($destinationFolder)\$($fileNameTemplate -f '*')"  # remove, put in win10qa without wildcard.

$sourcelinecount = Get-Content -Path $sourceFile | Measure-Object | Select-Object -ExpandProperty Count
$linecount = 0
$currentFileIndex = $fileIndexStart
$destinationFile = $null
$files = Get-Content -Path $sourceFile | ForEach-Object {
	If ((-not $destinationFile) -or ($linecount -eq $destinationFilesize)) {
		$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
		While (Test-Path -Path $destinationFile) {
			$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
		}
		Write-Host "Creating new file '$([IO.Path]::GetFileName($destinationFile))'"
		$linecount = 0
		$destinationFile
	}
	Add-Content -Path $destinationFile -Value $_
	$linecount++
}

$files | ForEach-Object {
	Write-Host "Starting new powershell instance with '$($_)' ..."
	Start-Process -FilePath powershell.exe -Verb runas -ArgumentList "-File `"$($workScript)`" -ComputerList `"$($_)`""
}

Clear-Content $sourceFile

Open in new window


Worker script:
Param(
	[String]$ComputerList
)
Write-Host "Processing $($ComputerList) ..."
# Demo only:
Start-Sleep -Seconds 10
# ...

Open in new window

oBdA - I'm sorry, could you please clarify a little, I don't have much experience passing arguments and am little confused about  how to setup the worker script..   


Would I put that block in my script1.ps1 or make changes to accept the input in my script1.ps1 in place of these lines? 

$ComputerList  = Get-Content "C:\Script\computerlist001.txt"
foreachrerun -Computers $ComputerList

Open in new window

Thanks for your patience, I put the full version of my script.ps1 for reference.


$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
         $n = 3 # Number of iterations through the patch process
         $m = 1 # Number of minutes for the script to pause between iterations of the loop


# (1) Function Test-Connection to determine if online****************|
        function Test-MyConnection {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]
            $ComputerName
            )
            if (Test-Connection $computername -Quiet -Count 2) {
                Write-Output "$($using:computer) is online"      
            }
            else {
                Write-Output "$($using:computer) is offline - check if powered on"
                Send-EmailOffline -ComputerName $computername  
            }
        }  # End (1)

# (2) Function to detect missing or pending SCCM updates*************|

#Function Detect-Missing {

#  } # End (2)

# (3) Function detect pending reboot*********************************|
Function Start-PendingReboot {

$rebootPending = Invoke-CimMethod -Namespace root/ccm/ClientSDK -ClassName CCM_ClientUtilities -MethodName DetermineIfRebootPending
    if ($rebootPending.RebootPending){
        Invoke-CimMethod -Namespace root/ccm/ClientSDK -ClassName CCM_ClientUtilities -MethodName RestartComputer
        Write-Output "$using:computer PENDING REBOOT detected, rebooting"
    }
  } # End (3)

# (4) Function run advert********************************************|
         Function Start-CCMRerunAdvertisement {
         [CmdLetBinding()]Param(
           [Parameter(Mandatory=$true)][string]$computerName,
           [Parameter(Mandatory=$false)][string]$advertisementId = "*",
           [Parameter(Mandatory=$false)][string]$packageId = "*",
           [Parameter(Mandatory=$false)][int]$maxRun = 1
         #[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
        )
        if($advertisementId -eq "*" -and $packageId -eq "*") {
            Write-Error "You must supply either an AdvertisementID or a PackageID"
            return "Missing Parameters"
            break
        }
        $searchString = "$advertisementId-$packageId-*"
        if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
            if($moreThanPing) {
                if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
                    Write-Error "System Offline"
                    Return "System Offline"
                    break
                }
            } else {
                Return "$Computername is from Advert, check PC manually."
                break
            }
        }
        Write-Verbose "Getting ID of ScheduleMessage on $computername"
        $schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
        $thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
        if(!$thisMsg) {
            Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
            Return "Cannot Find Advertisment"
            break
        }
        $thisMsg | % {
            [xml]$activeMessage = $_.activeMessage
            $amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
            $amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
            $amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
            $ScheduledMessageId = $_.ScheduledMessageId
            Write-Verbose  "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
            $softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
            $original_Rerun = $softwareDist.ADV_RepeatRunBehavior
            if($original_Rerun -ne "RerunAlways") {
                write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
                $softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
                $softwareDist.put() | Out-Null
            }
            Write-Verbose "Triggering Schedule on $computername"
            Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
            Write-Verbose "Sleeping for 5 seconds"
            Start-Sleep -Seconds 5
            if($original_Rerun -ne "RerunAlways") {
                Write-Verbose "Changing Rerun Status back to $original_Rerun"
                $softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
                $softwareDist.put() | Out-Null
            }

            #Return "Advert run on $computername"
        }
    } # End (4)

# (5) Function Invoke all available updates pending******************|
# Reference install all updates:  
#([wmiclass]'ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager').InstallUpdates([System.Management.ManagementObject[]] (get-wmiobject -query 'SELECT * FROM CCM_SoftwareUpdate' -namespace 'ROOT\ccm\ClientSDK')) | Out-Null
         function Invoke-SupInstall
            {
             Param
            (
             [String][Parameter(Mandatory=$True, Position=1)] $Computername,
             [String][Parameter(Mandatory=$True, Position=2)] $SupName
 
            )
            Begin
            {
             $AppEvalState0 = "0"
             $AppEvalState1 = "1"
             $ApplicationClass = [WmiClass]"root\ccm\clientSDK:CCM_SoftwareUpdatesManager"
            }
 
            Process
            {
            If ($SupName -Like "All" -or $SupName -like "all")
            {
             Foreach ($Computer in $Computername)
            {
                 #Write-Output "$using:computer (1) invoking adverts and installation of all updates, iteration # $i"
                 $Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState0)*" -or $_.EvaluationState -like "*$($AppEvalState1)*"})
                 Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer | Out-Null -ErrorAction SilentlyContinue
 
            }

            }
                 Else
 
            {
                 Foreach ($Computer in $Computername)
            {
                 #Write-Output "$using:computer (2) invoking adverts and installation of all updates, iteration # $i"
                 $Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState)*" -and $_.Name -like "*$($SupName)*"})
                 Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer | Out-Null -ErrorAction SilentlyContinue
           }
         }
       }
End {}
} # End (5)


# (6) Function Process monitoring, logs, reboot or shutdown**********|
    function Process-Monitor {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]$ComputerName
            )
            #Write-Output "$using:computer monitoring process count 'r'n"
            $processList = @'
                "Name",             "Expected",     "Running"
                "cmd",              "1",            "0"
                "TrustedInstaller", "1",            "0"
                "TIWorker",         "1",            "0"
                "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
            $splat = @{}
            $splat['ComputerName'] = $computerName
            #Write-Output "Monitoring processes on $($computerName)"
            Do  {
              $processList | ForEach-Object {
              $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
              }
              ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
              If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
                  Start-Sleep -Seconds 5
              }

              Write-Output "$($using:computer) process monitoring loop..."
               
            } Until (-not $running)

            $Today = (Get-Date).ToString('MM-dd-yyyy'),
            $ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
            $runningUser = & "whoami.exe"
            $Logfile = "c:\scripts\UPDATEQA.csv"
            $myobj = "" | Select "Computer", "ExactTime", "Admin"
            $myobj.Computer = $ComputerName
            $myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))
            $myobj.Admin = $runningUser

            $OutArray += $myobj
            $OutArray | Export-Csv $Logfile -NoTypeInformation -Append
            #Return "Process count finished on $computername"
 
            ###Restart computer for all iterations when process count is satisfied, except last defined, then issue shutdown.  Write to event logs
             if($i -eq 3){
               write-output "Final iteration #$i finished on $using:computer, Powering Off."
               Send-EmailComplete -ComputerName $computername
               ##################################################
               #SHUTDOWN /s /f /t 0 /m \\$using:computer/c
               ##################################################
               New-EventLog -LogName Application -Source UPDATEQA -ErrorAction SilentlyContinue
               Write-EventLog -LogName Application -Source UPDATEQA -EventId 555 -EntryType Information -Message "UPDATEQA $($computername) final iteration completed by $runninguser" -ComputerName $computername

            } else {

               New-EventLog -LogName Application -Source UPDATEQA -ErrorAction SilentlyContinue
               Write-EventLog -LogName Application -Source UPDATEQA -EventId 556 -EntryType Information -Message "UPDATEQA $($computername) iteration $i complete, run by $runninguser" -ComputerName $computername
               write-output "Iteration #$i  finished on $using:computer, initiating reboot."
               ##################################################
               Restart-Computer -Computer $using:computer -Force
               ##################################################
               Write-Output "$using:computer Sleeping for ($m*350)"
               Start-Sleep -Seconds ($m*350) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript
            }
        } # End (6)  


# (6a) Funtion Send Email ********************************************|
    function Send-EmailOffline {
         Send-MailMessage -From “admin@domain.com" -To “admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is offline! Scipt ran by $runninguser" -Body "$($ComputerName) is Offline! $runninguser, check PC is powered on."
    }

    function Send-EmailComplete {
         Send-MailMessage -From “admin@domain.com" -To “admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) completed by $runninguser" -Body "$using:computer completed, ready for deployment. Executed by $runningUser"
    } # End (6a)

# Main Function Calls ****************************************************|
            For ($i=1; $i -le $n; $i++) { # Begin looping $n times - $n is defined at the top of the InlineScript
                   
            # (1) Write-Output "$using:computer starting Test-MyConnection function for ping
            Test-MyConnection -ComputerName $using:Computer

            # (2) Write-Output "$using:computer detected missing or pending updates, iteration $i"
            #Detect-Missing -ComputerName $using:Computer

            # (3) Write-Output "$using:computer PENDING REBOOT detected, rebooting
            Start-PendingReboot -ComputerName $using:Computer

            # (4) Write-Output "$using:computer invoking advert iteration # $i"
            Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TIC62990"

            # (5) Write-Output "$using:computer invoking adverts and installation of all updates, iteration # $i"
            Invoke-SupInstall -Computername $using:computer -SUPName All

            #Write-Output "$using:computer Sleeping for ($m*30)"
            #Start-Sleep -Seconds ($m*30) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript

            # (6) Write-Output "$using:computer process monitoring iteration # $i"
            Process-Monitor -ComputerName $using:computer

        }
       } # End of InlineScript
    } # End of ForEach -parallel
} # End of initial ForEachRerun workflow
$ComputerList  = Get-Content "c:\scripts\computerlist.txt"
foreachrerun -Computers $ComputerList

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of oBdA
oBdA

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial