Replace String In Multiple Files
This script is a recent real world example whereby Powershell was able to be used to quickly find and replace a string in multiple .bat files (hence replace string in multiple files). The files are being used as login scripts for individual users on a legacy Active Directory environment. Each file needed to have the following actions performed against it:
- Check if an H: is being mapped
- If so, check if an E: is being mapped
- If not, remap the H: to E:
- Report on each .bat file whether a remapping was performed or not
We’ll end up using the cmdlets get-childitem, select-string and get-content with the .replace method. To control the output of our script we’ll use the PsCustomObject type accelerator which was first introduced in Powershell 3.0.
As an additional headache, the shared hosting of the .bat files doesn’t only include the in scope login scripts, so we need to determine a method to include only the files we want to modify. Luckily for us, they all follow the same name format which is, <userid>.bat where the <userid> is the users Active Directory SamAccountName attribute. For all of these users, this is an alphanumeric string exactly six characters long. With some basic regex combined with the where-object cmdlet, this becomes trivial to accomplish. You can see this on line 2 of the code below.
# Find all .bat files with 6 char length file names in the netlogon share of the fictional AD domain ad.powersloth.com $files = get-childitem \\ad.powersloth.com\netlogon\*.bat | where-object {$_.Name -match "^.{6}.bat$"} # Create a blank array to be used later $output = @() # Loop through the array of .bat files $files one by one foreach ($file in $files) { # First check if the string pattern "net use H:" exists in the .bat file. This is not case sensitive if (!(select-string $file -pattern "net use H:")) { # If our check above was false, we end up here. Print to the console and save the results in a variable for reporting later write-host "H: not mapped in $file" $HDriveCheck = "H Drive Was Not Mapped. No change needed." } else { # Otherwise, the H: was being mapped so we want to change it to E:. First we should check to make sure there is no E: already in use if (!(select-string $file -pattern "net use E:")) { # Our check was false so we are okay to change H: to E: # Print out to the console first write-host "H: mapped in $file" # Then change the string "H:" to "E:" using the .replace method on get-content and pipe the results back into the original file (get-content $file).replace("H:","E:") | out-file $file # Save the results into a variable for reporting later $HDriveCheck = "H: Drive remapped to E:." } else { # If we've ended up at this part of the loop, both the H: and the E: are already in use. # For now, we'll just report to the console and our reporting variable and not take any further action write-host "Warning: E: already in use in $file" $HDriveCheck = "E: already in use. No Change." } } # To save our results in a flexible format, we are going to build a new object using the PSCustomObject type accelerator $Result = [pscustomobject]@{ "Login Script" = $file "Result" = $HDriveCheck } #Finally combine the $result array and $output array before we loop back again $output += $result } # Once the loop is complete, we want to look at our results. # These two lines will export it to a csv and also print it on the console. $output | Export-Csv .\Results.csv -notype $output