I'm an Network Engineer an I wrote a small but effective PS Script to search logs(or any file for that matter) for textpatterns. Now this script only outputs the line, filename and so on. Now I wanted to extend the script so that when it finds a line, it'll tell me the line, filename and so on but also the contents of that line.
So it should look like this:
LineNumber Filename Path Pattern
---------- -------- ---- -------
4 190719_Success.log C:\skripte\190719_Success.log test
Text that's on Line 4 of the .log should appear here
5 190719_Success.log C:\skripte\190719_Success.log test
Text thats on Line 5 of the .log should appear here
Sorry for the formatting, I hope you get what I mean.
Since I'm relatively new to PS Scripting Im kinda lost how I should achieve this goal or if thats even possible.
Here is my code sofar:
Clear-Host
$Pfad = Read-Host "Bitte Pfad angeben" #Enter Directory Path to Search
$Suchbegriff = Read-Host "Suchbegriff eingeben" #Enter Pattern to search for
New-Item -ItemType directory -Path C:\Skripte -erroraction 'silentlycontinue' #create C:\Skripte Folder
Remove-Item -Path C:\Skripte\Suchergebnis.txt -erroraction 'silentlycontinue' #Cleanup from previous run
Remove-Item -Path C:\Skripte\Indizierung.csv -erroraction 'silentlycontinue' #Cleanup
cd $Pfad
echo $file.fullname
echo ""
select-string -Path .\*.* -Pattern "$Suchbegriff" -erroraction 'silentlycontinue' | Select-Object LineNumber,Filename,Path,Pattern | ft -wrap #Search the specified Directory
echo ""
while(($Create = Read-Host -Prompt "Unterordner durchsuchen? J für Ja, N für Nein") -ne "x") #Userinput if Subdirectorys should be searched aswell
{
switch ($Create)
{
'J'
{
Get-Childitem -erroraction 'silentlycontinue' | Get-ChildItem -Recurse -erroraction 'silentlycontinue' | Where-Object {$_.PSIsContainer} | Export-CSV -NoClobber -NoTypeInformation -Path C:\Skripte\Indizierung.csv #Get all Subdirectorys and put them in a CSV, two GCI are needed to reliably get all subdirectories.
$Files = import-csv -Delimiter ',' -Path C:\Skripte\Indizierung.csv #Import CSV
foreach ($File in $Files)
{
cd $file.fullname
echo $file.fullname
echo ""
select-string -Path .\*.* -Pattern "$Suchbegriff" -erroraction 'silentlycontinue' | Select-Object LineNumber,Filename,Path,Pattern | ft -wrap
echo ""
pause
exit
}
}
'n'
{
pause
Exit
}
}
}
Take a look at the following code. I added a loop for processing the files and some validation for the input + your request for the displaying the line content to the console.
Code adapted (#Theo: thanks for input)
#Get input
[System.String]$SearchPath = Read-Host -Prompt 'Enter path'
[System.String]$SearchPattern = Read-Host -Prompt 'Enter search pattern'
[System.String]$SearchRecurse = Read-Host -Prompt 'Search recurse (Y/N)'
#Validate input
if ((-not $SearchPath) -or (-not (Test-Path -Path $SearchPath)))
{
throw ('Path "' + $SearchPath + 'is not available!')
}
if ((-not $SearchPattern))
{
throw ('Search pattern is empty!')
}
if (('Y', 'N') -notcontains $SearchRecurse)
{
throw ('Search recurse parameter "' + $SearchRecurse + ' is not valid!')
}
#Get all files
Out-Host -InputObject 'Get all files...'
[PSCustomObject[]]$Files = #()
if ($SearchRecurse -eq 'Y')
{
$Files = Get-ChildItem -Path $SearchPath -File -Recurse -Force #Collect also files from subfolders
}
else
{
$Files = Get-ChildItem -Path $SearchPath -File -Force #Collect only files from the current folder
}
#Search for string
Out-Host -InputObject 'Search for string...'
[PSCustomObject[]]$Output = #()
foreach ($File in ($Files)) #Process each file
{
Out-Host -InputObject $File.FullName
$Output += Select-String -Path $File.FullName -Pattern $SearchPattern | Select-Object -Property LineNumber, Filename, Path, Pattern, Line
}
$Output | Format-Table -Wrap
Related
With this code I get the data but is not reflected on the txt file. I just get a bunch of Microsoft.PowerShell.Commands.Internal.Format.FormatStartData.
Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Instead the names of each file.
Here is the code:
param(
[string]$foldername = 'unknown'
)
if (Test-Path $foldername){
$location = Join-Path -Path $HOME -ChildPath "files.txt"
$files = Get-ChildItem $foldername | Where-Object {$_.PsIsContainer -ne $True} | Format-Table
Name
$amount = Get-ChildItem $foldername | Where-Object {$_.PsIsContainer -ne $True} | Measure-
Object
Add-Content -Path $location -Value $files
Write-Host $amount.count "filenames were written to file" $location
}
else {Write-Host "Sorry,", $foldername, "does not exist."
break
}
First get-childitem supports the switch File, no need to pipe and filter. The Format-Table is used to format the output displayed on the screen, so as you want to write to a file you do not need it.
Also only use break within a loop, if you want to stop the processing use return instead.
param(
[string]$foldername = 'unknown'
)
if (Test-Path $foldername){
$location = Join-Path -Path $HOME -ChildPath "files.txt"
#gets all files in the specified path
$files = Get-ChildItem $foldername -File
#counts the number of files
$amount = $files.count
#If you want the fullpath in the logfile use $files.fullname if you only want the name use $files.name
$files.FullName | Add-Content -Path $location
Write-Host "$amount filenames were written to file $location"
}
else {
Write-Host "Sorry, $foldername, does not exist."
return
}
I am fairly new to PowerShell and am having challenges trying to get a PS1 executable file to work. Running the script in a PowerShell console works completely fine and copy's items and creates the correct log filename.
The expectation would be to Right-click the PS1 file containing the script, run the script with "Run with PowerShell", and then allow the script to finish with a log file populated and files copied when user prompt selects yes.
At this point, there are no errors messages, other than the PS1 file script gets replaced by ton of unrecognizable symbols/characters and creates the log file as "Box Project Files.ps1JobFileLocations.log" instead of "JobFileLocations.log".
The PowerShell version being used is 5.1. Windows 10 OS. Set-ExecutionPolicy was set to Unrestricted and confirmed as Unrestricted for CurrentUser and LocalMachine. Unblock-File was also tried.
Below is the script that works in a PowerShell Console but not as a PS1 executable file.
# Drawing Tag Searches
$MechDWGFilterList = #('*IFC*','*mech*', '*permit*', '*final*')
$DatabaseFilterList = #('*field*','*software*')
# Root folder and destination folder
$JobNumber = '*'+(Read-Host -Prompt 'Enter in job number')+'*'
$srcRoot = 'C:\Users\username\Box\'
$JobRoot = (Get-ChildItem -Path $srcRoot -Filter "*Active Projects*" -Recurse -Directory -Depth 1).Fullname
$dstRoot = $MyInvocation.MyCommand.Path
# Find job numer pdf file
$JobFolder = (Get-ChildItem -Path $JobRoot -Filter "$JobNumber" -Recurse -Directory -Depth 0).Fullname
$Logfile = $dstRoot+"JobFileLocations.log"
$reply = Read-Host -Prompt "Make a copy of relevant project files to local drive?[y/n]"
# Find sub-folder from job folder
$ProposalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Proposals*" -Recurse -Directory).Fullname
$MechDWGFolder = (Get-ChildItem -Path $JobFolder -Filter "*Plans*" -Recurse -Directory).Fullname
$SubmittalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Submittal*" -Recurse -Directory).Fullname
$DatabaseFolder = (Get-ChildItem -Path $JobFolder -Filter "*Backup*" -Recurse -Directory).Fullname
$EstimateFolder = (Get-ChildItem -Path $JobFolder -Filter "*Estimate*" -Recurse -Directory).Fullname
# Find files from list
$ProposalList = Get-ChildItem -Path $ProposalFolder -Filter '*proposal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$MechDWGList = Get-ChildItem -Path $MechDWGFolder -Filter *.pdf -r | Sort-Object -Descending -Property LastWriteTime
$SubmittalList = Get-ChildItem $SubmittalFolder -Filter '*submittal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$DatabaseList = Get-ChildItem $DatabaseFolder -Filter *.zip -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$EstimateList = Get-ChildItem -Path $EstimateFolder -Filter *.xl* -r | Sort-Object -Descending -Property LastWriteTime
# Log file path location and copy file to local directory
# Function to add items to a log text file
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
# Log file path location and copy file to local directory
LogWrite "::==========================================::`n|| Project Document Paths ||`n::==========================================::"
LogWrite "`nNote: If a section has more than one file path, files are listed from most recent to oldest.`n"
LogWrite "----------Scope Document/Proposal(s)----------"
foreach ($file in $ProposalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------Mechanical Drawing(s)------------"
foreach ($file in $MechDWGList)
{
# Where the file name contains one of these filters
foreach($filter in $MechDWGFilterList)
{
if($file.Name -like $filter)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
}
}
LogWrite "`n-------------Controls Submittal(s)------------"
foreach ($file in $SubmittalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------------Database-------------------"
foreach ($file in $DatabaseList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n------------------Estimate(s)-----------------"
foreach ($file in $EstimateList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
Could someone help me understand what is wrong with running the script as a PS1 file?
The problem with your script is this particular line:
$dstRoot = $MyInvocation.MyCommand.Path
$MyInvocation.MyCommand.Path resolves the rooted filesystem path to the script itself - which is why you get Box Project Files.ps1 (presumably the name of the script) in the log path.
The get the path of the parent directory of any file path, you can use Split-Path -Parent:
$dstRoot = Split-Path -LiteralPath $MyInvocation.MyCommand.Path -Parent
That being said, since Windows PowerShell 3.0, both the directory and script file paths have been available via the $PSCommandPath and $PSScriptRoot automatic variables, so you can simplify the code to just:
$dstRoot = $PSScriptRoot
Im currently trying to get a script to do the following:
search a file location and obtain any .txt,.ini,.config file.
filter this file on an input from the user.
move all the files to a location.
Im having a problem trying to feed in the variable and its probably really simple but im currently struggling to figure out what I need to do without splitting the string up manually. any ideas?
$QQ = Read-Host -Prompt "String your searching for:"
$QL = Read-Host -Prompt "Enter the file location you wish to search:"
$FT = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".txt"} | % {$_.fullname}
$FI = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".ini"} | % {$_.fullname}
$FC = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".config"} | % {$_.fullname}
$FTS = Get-Content -Path "$FT" -Filter "$QQ"
$FIS = Get-Content -Path "$FI" -Filter "$QQ"
$FCS = Get-Content -Path "$FC" -Filter "$QQ"
$FD = "C:\Search-$QQ"
$FD1 = Get-ChildItem $FD
function folder {
if ($FD -eq $Null) {New-Item "$FD" -ItemType directory}}
function search{
if ($FTS -ne $null){Copy-Item -Path $ft -Destination "$fd" | Write-Host "$FT" | Format-List}
if ($FIS -ne $null){Copy-Item -path $fi -Destination "$fd" | Write-Host "$FI" | Format-List}
if ($FCS -ne $null){Copy-Item -Path $fc -destination "$fd" | Write-Host "$FC" | Format-List}
}
folder
search;
An example of the error being received is: (obviously an issue with it treating the multiple files in the string as one)
Get-Content : Cannot find path 'C:\test\Test\1c.config C:\test\Test\2c.config C:\test\Test\3c.config' because it does not exist.
right off the bat I see the folder function will not work property as $FD will never be null since you assign a string to it, if you need to check if the folder exists use Test-Path. but in relation to your actual script the problem is your use of get-content which is trying to read all of your files contents into an array of strings. from the looks of it you should just be working with the arrays returned by Get-ChildItem and use Test-Path to check your paths rather than that weird Get-Content method you are using(since the content of the files doesn't matter, just whether they exist or not). You also need to incorporate a loop to act on each array element individually rather than trying to work on them in groups like you are.
(This Script is to mass search application files to find values like server id's or passwords hardcoded etc)
Fixed code:
$QQ = Read-Host -Prompt "String your searching for:"
$QL = Read-Host -Prompt "Enter the file location you wish to search:"
$FT = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".txt"} | % {$_.fullname}
$FI = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".ini"} | % {$_.fullname}
$FC = Get-ChildItem -Path "$QL" -recurse | where {$_.extension -eq ".config"} | % {$_.fullname}
$FTS = Get-Content -Path $FT -Filter "$QQ"
$FIS = Get-Content -Path $FI -Filter "$QQ"
$FCS = Get-Content -Path $FC -Filter "$QQ"
$FD = "C:\Support\Search-$QQ"
$FD1 = Test-Path $FD
function folder {
if ($FD1 -eq $false) {New-Item "$FD" -ItemType directory}
}
function search{
if ($FTS -ne $null){Copy-Item -Path $ft -Destination "$fd" | Write-Host "$FT" | Format-List}
if ($FIS -ne $null){Copy-Item -path $fi -Destination "$fd" | Write-Host "$FI" | Format-List}
if ($FCS -ne $null){Copy-Item -Path $fc -destination "$fd" | Write-Host "$FC" | Format-List}
}
folder
search;
try this
$QL = Read-Host -Prompt "Enter the file location you wish to search:"
if (-not (Test-Path $QL))
{
write-host "Specified path ($QL) doesnt exists "
return;
}
$QQ = Read-Host -Prompt "String your searching for:"
$FD = "C:\Search-$QQ"
New-Item "C:\Search-$QQ" -ItemType directory -Force | out-null
Get-ChildItem -Path "$QL" -recurse -include "*.txt", "*.ini", "*.config" |
select-string -Pattern "$QQ" -SimpleMatch |
%{ Copy-Item -Path $_.Path -Destination "$fd" -Force ; $_.Path }
I put together a Robocopy script to backup data, but the problem I am having is the script is reading the parent folder and trying to execute the robocopy filters and error out.
what I would like for it to do is indent one and start copying the subfolder and use the filter there. the file structure is
Users\tim jones, sam adams
I just want the tim jones folder and the sam adams user folders with the Robocopy filter added to it. any help is welcomed
$Comp = Read-Host 'Please enter a computer name or IP'
do{
If (Test-Connection $Comp -quiet -Count 1) {
Write-host 'The host responded' -ForegroundColor "yellow"
Read-Host 'Press any key to continue...' | Out-Null
$ping = "ture"
Clear-Host
Get-WmiObject -ComputerName $Comp -class Win32_NetworkLoginProfile | Select- Object Name,#{label='LastLogon';expression={$_.ConvertToDateTime($_.LastLogon)}}
New-Item -Path \\skyzone\t$\sky\ -ItemType directory
$SourcePath = "\\$Comp\C$\Users\";
$TargetPath = "\\skyzone\t$\sky";
#Users libraries
$PicturesLibrary = ("\Pictures");
$Downloads = ("\Downloads");
$Favorites = ("\Favorites");
$Documents = ("\Documents");
$Desktop = ("\Desktop");
$Video = ("\Videos");
#User Outlook files and logs
$Outlook = ("\AppData\Local\Microsoft\Outlook")
$argsFromFolders = ("$SourcePath\$PicturesLibrary","$SourcePath\$Downloads","$SourcePath\$Favorites","$SourcePath\$Documents","$SourcePath\$Desktop","$SourcePath\$Video" ,"$SourcePath\$Outlook");
$argsToFolders = ("$TargetPath\Pictures","$TargetPath\Downloads","$TargetPath\Favorites","$TargetPath\Documents","$TargetPath\Desktop","$TargetPath\Videos","$TargetPath\Outlook");
For ($i=0; $i -lt $argsFromFolders.Length; $i++) {
Write-Host -ForegroundColor Green "Processing" $argsFromFolders[$i] "To" $argsToFolders[$i];
robocopy $argsFromFolders[$i] $argsToFolders[$i] *.* /MT:16 /XJ *.pst /R:3 /W:1 /NP /e /xf *.vmdk *.vmem *.iso *.exe *.ost desktop.ini /tee /Log+:
}
Your question is unclear and the sample is incomplete. You refer to $user which is never created and there are no robocopy-calls here. Because of this I can't give you a complete solution.
Take a look at this example source-path in your code:
$SourcePath = "\\localhost\C$\Users\";
$PicturesLibrary = ("\Pictures");
"$SourcePath\$PicturesLibrary"
#Output
\\localhost\C$\Users\\\Pictures
There are too many backslashes in the spot where you joined the two strings. Remove them from the variables or use Join-Path
$SourcePath = "\\localhost\C$\Users\"
$PicturesLibrary = "\Pictures"
Join-Path $SourcePath $PicturesLibrary
#Output
\\localhost\C$\Users\Pictures
Or
$SourcePath = "\\localhost\C$\Users"
$PicturesLibrary = "Pictures"
"$SourcePath\$PicturesLibrary"
#Output
\\localhost\C$\Users\Pictures
The username is missing. You need to use Get-Childitem on the $sourcePath to get the list of users in the first place and loop through those.
Sample:
$SourcePath = "\\localhost\C$\Users\"
Get-ChildItem -Path $SourcePath |
#Get folders only... And exlude public?
Where-Object { $_.PSIsContainer -and $_.Name -ne 'Public' } |
ForEach-Object {
#Code to run for each user-folder
$PicturesLibrary = ("Pictures")
Join-Path $_.FullName $PicturesLibrary
}
\\localhost\C$\Users\Default.migrated\Pictures
\\localhost\C$\Users\defaultuser0\Pictures
\\localhost\C$\Users\frode\Pictures
Update: Try something like this (untested).
$Comp = Read-Host 'Please enter a computer name or IP'
If (Test-Connection $Comp -quiet -Count 1) {
Write-host 'The host responded' -ForegroundColor "yellow"
Read-Host 'Press any key to continue...' | Out-Null
Clear-Host
Get-WmiObject -ComputerName $Comp -class Win32_NetworkLoginProfile | Select- Object Name,#{label='LastLogon';expression={$_.ConvertToDateTime($_.LastLogon)}}
$TargetPath = '\\skyzone\t$\sky'
if(-not (Test-Path -Path $TargetPath -PathType Container)) { New-Item -Path $TargetPath -ItemType Directory | Out-Null }
$Folderlist = ("Pictures","Downloads","Favorites","Documents","Desktop","Videos","Outlook");
Get-ChildItem -Path "\\$Comp\C$\Users\" |
#Get folders only... And exclude public?
Where-Object { $_.PSIsContainer -and $_.Name -ne 'Public' } |
ForEach-Object {
$user = $_.Name
foreach ($folder in $Folderlist) {
$from = Join-Path $_.FullName $folder
$to = Join-Path $TargetPath "$user\$folder"
Write-Host -ForegroundColor Green "Processing '$from' To '$to'"
robocopy $from $to *.* /MT:16 /XJ *.pst /R:3 /W:1 /NP /e /xf *.vmdk *.vmem *.iso *.exe *.ost desktop.ini /tee /Log+:\\apgrwkate1090f\t$\dren\$user\backup.txt
}
}
}
Really need help creating a script that backs up, and shoots out the error along the file that did not copy
Here is what I tried:
Creating lists of filepaths to pass on to copy-item, in hopes to later catch errors per file, and later log them:
by using $list2X I would be able to cycle through each file, but copy-item loses the Directory structure and shoots it all out to a single folder.
So for now I am using $list2 and later I do copy-item -recurse to copy the folders:
#create list to copy
$list = Get-ChildItem -path $source | Select-Object Fullname
$list2 = $list -replace ("}"),("")
$list2 = $list2 -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\DirList.txt -InputObject $list2
#create list crosscheck later
$listX = Get-ChildItem -path $source -recurse | Select-Object Fullname
$list2X = $listX -replace ("}"),("")
$list2X = $list2X -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\FileDirList.txt -InputObject $list2X
And here I would pass the list:
$error.clear()
Foreach($item in $list2){
Copy-Item -Path $item -Destination $destination -recurse -force -erroraction Continue
}
out-file -FilePath g:\backuplog\errorsBackup.txt -InputObject $error
Any help with this is greatly appreciated!!!
The answer to complex file-copying or backup scripts is almost always: "Use robocopy."
Bill
"Want to copy all the items in C:\Scripts (including subfolders) to C:\Test? Then simply use a wildcard character..."
Next make it easier on yourself and do something like this:
$files = (Get-ChildItem $path).FullName #Requires PS 3.0
#or
$files = Get-ChildItem $path | % {$_.Fullname}
$files | Out-File $outpath
well it took me a long time, considering my response time. here is my copy function, which logs most errors(network drops, failed copies , etc) the copy function , and targetobject.
Function backUP{ Param ([string]$destination1 ,$list1)
$destination2 = $destination1
#extract new made string for backuplog
$index = $destination2.LastIndexOf("\")
$count = $destination2.length - $index
$source1 = $destination2.Substring($index, $count)
$finalstr2 = $logdrive + $source1
Foreach($item in $list1){
Copy-Item -Container: $true -Recurse -Force -Path $item -Destination $destination1 -erroraction Continue
if(-not $?)
{
write-output "ERROR de copiado : " $error| format-list | out-file -Append "$finalstr2\GCI-ERRORS-backup.txt"
Foreach($erritem in $error){
write-output "Error Data:" $erritem.TargetObject | out-file -Append "$finalstr2\GCI- ERRORS-backup.txt"
}
$error.Clear()
}
}
}