Searching through a text file - powershell

I have a script that searches for the lastest modified log file. It then is suppose to read that text file and pick up a key phrase then display the line after it.
So far i have this
$logfile = get-childitem 'C:\logs' | sort {$_.lastwritetime} | where {$_ -notmatch "X|Zr" }| select -last 1
$error = get-content $logfile | select-string -pattern "Failed to Modify"
an example line it reads is this
20150721 12:46:26 398fbb92 To CV Failed to Modify
CN=ROLE-x-USERS,OU=Role Groups,OU=Groups,DC=gyp,DC=gypuy,DC=net
MDS_E_BAD_MEMBERSHIP One or more members do not exist in the directory
They key bit of information im trying to get here is
Can anyone help?
Thanks

Try this:
$error = get-content $logfile |
Where-Object { $_ -like "*Failed to Modify*" } |
Select-Object -First 1
This is provided you are looking for the first match in the file. The Select-String cmdlet returns a MatchInfo object. Depending on your requirements there might be no reason to add that level of complexity if you're just looking to pull the first occurrence of this error in the file.
Failing this, my recommendation would be to debug this and step through it. Break on the Get-Content call and see what $logfile is. Run Get-Content $logfile and see what that content looks like. Then do your Select-String on that output. See what MatchInfo.ToString() looks like. Maybe you'll see some disconnect.
Again, my recommendation would be to just parse manually through the file and work with the Where-Object cmdlet at this point.

This shoul work:
get-childitem 'c:\logs' | where {$_.Name -notmatch "X|Zr" } | sort {$_.lastwritetime} | select -last 1 | select-string "Failed to Modify"
But I don't like "X|Zr" part. If your log files have .txt extension, it'll not list them because you're saying you don't want any file containing "x" or "zr" in entire name. Use $_.BaseName (name without extension), or modify regular expression.

Related

Powershell -- Get-ChildItem Directory full path and lastaccesstime

I am attempting to output full directory path and lastaccesstime in one line.
Needed --
R:\Directory1\Directory2\Directory3, March 10, 1015
What I am getting --
R:\Directory1\Directory2\Directory3
March 10, 1015
Here is my code, It isn't that complicated, but it is beyond me.
Get-ChildItem -Path "R:\" -Directory | foreach-object -process{$_.FullName, $_.LastAccessTime} | Where{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) } | Out-File c:\temp\test.csv
I have used foreach-object in the past in order to ensure I do not truncate the excessively long directory names and paths, but never used it when pulling two properties. I would like the information to be on all one line, but haven't been successful. Thanks in advance for the assist.
I recommend filtering (Where-Object) before selecting the properties you want. Also I think you want to replace ForEach-Object with Select-Object, and lastly I think you want Export-Csv rather than Out-File. Example:
Get-ChildItem -Path "R:\" -Directory |
Where-Object { $_.LastAccessTime -lt [DateTime]::Today.AddYears(-2) } |
Select-Object FullName,LastAccessTime |
Export-Csv C:\temp\test.csv -NoTypeInformation
We can get your output on one line pretty easily, but to make it easy to read we may have to split your script out to multiple lines. I'd recommend saving the script below as a ".ps1" which would allow you to right click and select "run with powershell" to make it easier in the future. This script could be modified to play around with more inputs and variables in order to make it more modular and work in more situations, but for now we'll work with the constants you provided.
$dirs = Get-ChildItem -Path "R:\" -Directory
We'll keep the first line you made, since that is solid and there's nothing to change.
$arr = $dirs | Select-Object {$_.FullName, $_.LastAccessTime} | Where-Object{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) }
For the second line, we'll use "Select-Object" instead. In my opinion, it's a lot easier to create an array this way. We'll want to deal with the answers as an array since it'll be easiest to post the key,value pairs next to each other this way. I've expanded your "Where" to "Where-Object" since it's best practice to use the full cmdlet name instead of the alias.
Lastly, we'll want to convert our "$arr" object to csv before putting in the temp out-file.
ConvertTo-CSV $arr | Out-File "C:\Temp\test.csv"
Putting it all together, your final script will look like this:
$dirs = Get-ChildItem -Path "C:\git" -Directory
$arr = $dirs | Select-Object {$_.FullName, $_.LastAccessTime} | Where{ $_.LastAccessTime -lt [datetime]::Today.AddYears(-2) }
ConvertTo-CSV $arr | Out-File "C:\Temp\test.csv"
Again, you can take this further by creating a function, binding it to a cmdlet, and creating parameters for your path, output file, and all that fun stuff.
Let me know if this helps!

PowerShell script file modify time>10h and return a value if nothing is found

I am trying to compose a script/one liner, which will find files which have been modified over 10 hours ago in a specific folder and if there are no files I need it to print some value or string.
Get-ChildItem -Path C:\blaa\*.* | where {$_.Lastwritetime -lt (date).addhours(-10)}) | Format-table Name,LastWriteTime -HideTableHeaders"
With that one liner I am getting the wanted result when there are files with
modify time over 10 hours, but I also need it to print value/string if there are
no results, so that I can monitor it properly.
The reason for this is to utilize the script/one liner for monitoring purposes.
Those cmdlet Get-ChildItem and where clause you have a would return null if nothing was found. You would have to account for that separately. I would also caution the use of Format-Table for output unless you are just using it for screen reading. If you wanted a "one-liner" you would could this. All PowerShell code can be a one liner if you want it to be.
$results = Get-ChildItem -Path C:\blaa\*.* | where {$_.Lastwritetime -lt (date).addhours(-10)} | Select Name,LastWriteTime; if($results){$results}else{"No files found matching criteria"}
You have an added bracket in your code, that might be a copy artifact, I had to remove. Coded properly would look like this
$results = Get-ChildItem -Path "C:\blaa\*.*" |
Where-Object {$_.Lastwritetime -lt (date).addhours(-10)} |
Select Name,LastWriteTime
if($results){
$results
}else{
"No files found matching criteria"
}

Why won't it rename the file? Powershell

Can someone tell me why this script won't work?
Get-ChildItem "\\fhnsrv01\home\aborgetti\Documentation\Stage" -Filter *.EDIPROD | `
Foreach-Object{
$content = Get-Content $_.FullName
#filter and save content to a new file
$content | Where-Object {$_ -match 'T042456'} | Rename-Item `
($_.BaseName+'_834.txt')
I found this syntax from another question on here and changed the environment variables.
For some reason it won't change the name of the file. The filename is
'AIDOCCAI.D051414.T042456.MO.EDIPROD'
Help much appreciated.
UPDATE
Thanks to TheMadTechnician I was able to get some working stuff. Great stuff actually. Figure I should share with the world!
#Call Bluezone to do file transfer
#start-process "\\fhnsrv01\home\aborgetti\Documentation\Projects\Automation\OpenBZ.bat"
#Variable Declarations
$a = Get-Date
$b = $a.ToString('MMddyy')
$source = "\\fhnsrv01\home\aborgetti\Documentation\Stage\"
$dest = "\\fhnsrv01\home\aborgetti\Documentation\Stage\orig"
#Find all the files that have EDIPROD extension and proceed to process them
#First copy the original file to the orig folder before any manipulation takes place
Copy-item $source\*.EDIPROD $dest
# Now we must rename the items that are in the table
Switch(GCI \\fhnsrv01\home\aborgetti\Documentation\Stage\*.EDIPROD){
{(GC $_|Select -first 1) -match "834*"}{$_ | Rename-Item -NewName {$_.BaseName+'_834.txt'}}
{(GC $_|Select -first 1) -match "820*"}{$_ | Rename-Item -NewName {$_.BaseName+'_820.txt'}}
}
Get-ChildItem's -Filter has issues, I really hesitate to use it in general. If it were up to me I'd do something like this:
Get-ChildItem "\\fhnsrv01\home\aborgetti\Documentation\Stage" |
?{$_.Extension -match ".EDIPROD" -and $_.name -match "T042456"}|
%{$_.MoveTo($_.FullName+"_834.txt")}
Well, I would put it all on one line, but you can line break after the pipe and it does make it a little easier to read, so there you have it. I'm rambling, sorry.
Edit: Wow, I didn't even address what was wrong with your script. Sorry, kind of distracted at the end of my work day here. So, why doesn't your script work? Here's why:
You pull a file and folder listing for the chosen path. That's great, it should work, more or less, I have almost no faith in the -Filter capabilities of the file system provider, but anyway, moving on!
You take that list and run it through a ForEach loop processing each file that matches your filter as such:
You read the contents of the file, and store them in the variable $content
You run the contents of the file, line by line, there a Where filter looking for the text "T042456"
For each line that matches that text you attempt to rename something to that line's basename plus _834.txt (the line of text is a string, it doesn't have a basename property, and it's not an object that can be renamed, so this is going to fail)
So, that's where the issue is. You're pulling the contents of the file, and parsing that line by line trying to match the text instead of matching against the file name. If you removed Everything after the first pipe up to the Where statement, and then for your rename-item put -newname before your desired name, and change the ( ) to { } that goes around the new name, and you would be set. Your code would work. So, your code, modified as I said, would look like:
Get-ChildItem "\\fhnsrv01\home\aborgetti\Documentation\Stage" -Filter *.EDIPROD |
Where-Object {$_ -match 'T042456'} | Rename-Item -NewName {$_.BaseName+'_834.txt'}
Though I have a feeling you want $.Name and not $.BaseName. Using $_.BaseName will leave you with (to use your example file name):
'AIDOCCAI.D051414.T042456.MO_834.txt`
Edit2: Really that's a whole different question, how to match multiple criteria, but the question is here, I'm here, why not just get it done?
So, you have multiple criteria for matching the file names. That really doesn't affect your loop to be honest, what it does affect is the Where statement. If you have multiple options what you probably want is a RegEx match. Totally doable! I'm only going to address the Where statement (?{ }) here, this won't change anything else in the script.
We leave the extension part, but we're going to need to modify the file name part. With RegEx you can match against alternative text by putting it in parenthesis and splitting up the various options with a pipe character. So it would look something like this:
"(T042456|T195917|T048585)"
Now we can incorporate that into the rest of the Where statement and it looks like this:
?{$_.Extension -match ".EDIPROD" -and $_.name -match "(T042456|T195917|T048585)"}
or in your script:
Where-Object {$_ -match "(T042456|T195917|T048585)"}
Edit3: Hm, need the first line for the qualifier. That complicates things a bit. Ok, so what I'm thinking is to get our directory listing, get the first line of each file with the desired extension, make an object that has two properties, the first property is the fileinfo object for the file, and the other property will be the first line of the file. Wait, I think we can do better. Switch (GCI *.EDIPROD){(get-content|select -first 1) -match 820}{Rename 820};{blah blah -match 834}{rename 834}}. Yeah, that should work. Ok, actual script, not theoretical gibberish script time. This way if you have other things to look for you can just add lines for them.
Switch(GCI \\fhnsrv01\home\aborgetti\Documentation\Stage\*.EDIPROD){
{(GC $_|Select -first 1).substring(177) -match "^834"}{$_ | Rename-Item -NewName {"834Dailyin$b"};Continue}
{(GC $_|Select -first 1).substring(177) -match "^820"}{$_ | Rename-Item -NewName {$_.BaseName+'_820.txt'};Continue}
}
Again, if you want the EDIPROD part to remain in the file name change $_.BaseName to $_.Name. Switch is pretty awesome if you're trying to match against different things and perform different actions depending on what the results are. If you aren't familiar with it you may want to go flex your google muscles and check it out.
Hm, alternatively we could have gotten the first line inside the Where filter, run a regex match against that, and renamed the file based on the regex match.
GCI \\fhnsrv01\home\aborgetti\Documentation\Stage\*.EDIPROD | ?{(GC $_ | Select -First 1) -match "(820|834)"}|Rename-Item -NewName {$_.Name+"_"+$Matches[1]+".txt"}
Then you just have to update the Where statement to include anything you're trying to match against. That's kind of sexy, though not as versatile as the switch. But for just simple search and rename it works fine.
Try it like this way
Get-ChildItem -Filter "*T042456*" -Recurse | % {Rename-Item $_ "$_ _834.txt"}

Replace lines with specific string and save with the same name

I'm working with an application that creates a log file. Due to an error in the software itself, it keeps producing three errors I'm not interested in. Each line has a unique identifier so I can't just replace the line since each one is different.
I have two main issues with this: I need to save it with the same name, and while it works the file should be available (in case the logger needs to write something).
I can't hard-code the original app to prevent it from writing that part of the log.
I have tried so far:
Get-Content log.log | Where-Object {$_-notmatch 'ERROR1' -And $_-notmatch 'ERROR2' -And $_-notmatch 'ERROR3' } `|Set-Content log_stripped.log
^ It only works if the output file has a different name.
Get-Content error.log | foreach-object { Where-Object {$_-notmatch 'ERROR1' -And $_-notmatch 'ERROR2' -And $_-notmatch 'ERROR3' } } | Set-Content error.log
^ This one froze my PS session.
I also tried reading the file to a variable:
$logcontent = ${h:error.log}
but I got System.OutOfMemoryException.
Ideally, what I need is something that reads the log file, takes away all the lines I don't want, and then save it with its original name.
Ideas? (Keep in mind that the log file is +/- 900 MB with the unnecesary data and 45mb once I strip the data with the first method - but I need it to save the file with its original name)
You can't save the file back to the same name while you're still reading from it, which means you'd have to read the whole 900MB into memory before you start writing. Not a good idea.
Try this:
Remove-Item log_stripped.log
Get-Content log.log -ReadCount 1000 |
foreach {$_ -notmatch 'ERROR1|ERROR2|ERROR3' | Add-Content log_stripped.log }
Remove-item log.log
Rename-Item log_stripped.log log.log
I know you said you want to save to the same filename, but if the reason you want that is that you want the log to be continuously updated, then you could do the following:
Get-Content -Wait log.log |
? {$_ -notmatch 'ERROR1|ERROR2|ERROR3' } |
Out-File log_stripped.log
Note the -Wait on the Get-Content.
log_stripped.log will be continuously updated as log.log is updated.

Powershell refer to objects in table

I have a script I am working on that will output all fileNames and lineNumbers of a key word search.
$Paths = gci . *.* -rec | where { ! $_.PSIsContainer } | resolve-path
foreach($path in $Paths)
{
$ftp += Select-String -Path $Path -Pattern "FTP"
}
$ftpgroups = $ftp | select fileName,LineNumber | Format-Table -groupBy Filename
I decided to go with ft -groupby because group-object was not working correctly. But I need a way to reference this table so I can put it into a csv. When using the get-member commandlet it only gives me properties of formating. The ideal output for this is to have 1 fileName matched up to a group of fileLines. That way I can match that up to the path (which group-object worked succesfully on).
I am open to new ideas if I am going about this the wrong way. Thank you in advanced, hope it doesn't cause you as much trouble as it has me.
As you have found, the output of any of the Format-* cmdlets is formatting objects. These objects are meant for display to the console and not further manipulation. You really need Group-Object for this. In what way wasn't it working for you? I would think, this would work:
$ftpgroups = $ftp | Select Filename,LineNumber | Group Filename