output filename, not string with select-string - powershell

I'm using powershell to "grep" my source code for a particular string. If the string is in the file, I would like the name of the file, not the line of code that contains the string.
I would also like the name of the file, just once, not listed for as many times as the file exists.
I'm currently using:
gci . -include "*.sql" -recurse | select-string -pattern 'someInterestingString'
Now I understand that the output of select-string is some sort of ojbect, and what I'm seeing in the console is, i'm guessing, the ToString() of that object. I assume that I could use format-table to control the output of the select-string, and I suppose sort to get distinct values only.
but that's a lot of guessing.

I don't think I completely understand what you're trying to do. If you want the output grouped by file, you can pipe into Format-Table with the -GroupBy parameter:
gci . -include "*.sql" -recurse `
| select-string -pattern 'someInterestingString' `
| Format-Table -GroupBy Path
If you want to get only the names of the files that match without any other info, you can use Select-Object with the -Unique parameter:
gci . -include "*.sql" -recurse `
| select-string -pattern 'someInterestingString' `
| Select-Object -Unique Path
If you're interested in only the file name, regardless whether the name itself appears multiple times in your hierarchy, then you can select the Filename property instead.
Note: The Get-Member cmdlet is a great help in figuring out what properties exist on an object:
gci . -include "*.sql" -recurse `
| select-string -pattern 'someInterestingString' `
| Get-Member
You can also use its alias gm instead.

When I'm doing this I just use the -List parameter - yes it does display the line of code but you only get one line per file (no matter how many matches there are):
PS> Get-ChildItem . -r *.cs | Select-String XmlNode -list
Commands\SnapinHelp\CmdletInfo.cs:27: public List<XmlNode> InputTypes;
Commands\SnapinHelp\GetSnapinHelpCommand.cs:124: WriteXmlNodeList(c...
Commands\SnapinHelp\ParameterInfo.cs:73: XmlNode FindNode(XmlDocument doc)
Commands\Xml\XmlCommandBase.cs:65: RegisterInputType<XmlNode>(Proce...
If you want the path:
PS> Get-ChildItem . -r *.cs | Select-String XmlNode -list |
Format-Table Path
Path
--------
C:\Users\Keith\Pscx\Src\PscxSnapin\Commands\SnapinHelp\CmdletInfo.cs
C:\Users\Keith\Pscx\Src\PscxSnapin\Commands\SnapinHelp\GetSnapinHelpCommand.cs
C:\Users\Keith\Pscx\Src\PscxSnapin\Commands\SnapinHelp\ParameterInfo.cs
C:\Users\Keith\Pscx\Src\PscxSnapin\Commands\Xml\XmlCommandBase.cs
Or if you really only want the filename:
PS> Get-ChildItem . -r *.cs | Select-String XmlNode -list |
Format-Table Filename
Filename
--------
CmdletInfo.cs
GetSnapinHelpCommand.cs
ParameterInfo.cs
XmlCommandBase.cs

I found it easier to do
(...|select-string "search").Path

Related

I need my Get-ChildItem string search command print file names along with their Date Modified values, sorted

I spent quite some time searching for the solution of my problem, but found nothing. I have one single folder with mostly .html files, and I frequently need to search to find the files that contain certain strings. I need the search result to be displayed with just the file name (as the file will only be in that one folder) and file's last write time. The list needs to be sorted by the last write time. This code works perfectly for finding the correct files
Get-ChildItem -Filter *.html -Recurse | Select-String -pattern "keyWord string" | group path | select name
The problem with it is that it displays the entire path of the file (which is not needed), it does not show the last write time, and it is not sorted by the last write time.
I also have this code
Get-ChildItem -Attributes !Directory *.html | Sort-Object -Descending -Property LastWriteTime | Select-Object Name, LastWriteTime
That code prints everything exactly as I want to see it, but it prints all the file names from the folder instead of printing only the files that I need to find with a specific string in them.
Since you are only using Select-String to determine if the text exists in any of the files move it inside a Where-Object filter and use the -Quiet parameter so that it returns true or false. Then sort and select the properties you want.
Get-ChildItem -Filter *.html |
Where-Object { $_ | Select-String -Pattern 'keyWord string' -Quiet } |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
For multiple patterns one way you can do it is like this
Get-ChildItem -Filter *.html |
Where-Object {
($_ | Select-String -Pattern 'keyWord string' -Quiet) -and
($_ | Select-String -Pattern 'keyWord string #2' -Quiet)
} |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
And another way using Select-String with multiple patterns which may be a bit faster
$patterns = 'keyword 1', 'keyword 2', 'keyword 3'
Get-ChildItem -Filter *.html |
Where-Object {
($_ | Select-String -Pattern $patterns | Select-Object -Unique Pattern ).Count -eq $patterns.Count
} |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
If you don't care about it being a bit redundant, you can Get-ChildItem the results after your searching:
Get-ChildItem -Filter *.html -Attributes !Directory -Recurse | Select-String -Pattern "keyWord string" | group path | foreach {Get-ChildItem $_.Name } | Sort-Object -Descending LastWriteTime | Select Name,LastWriteTime
After you Select-String you get the attributes of that object instead of the original, so we're taking the results of that object and passing it back into the Get-ChildItem command to retrieve those attributes instead.

Copy files listed in a txt document, keeping multiple files of the same name in PowerShell

I have a bunch of lists of documents generated in powershell using this command:
Get-ChildItem -Recurse |
Select-String -Pattern "acrn164524" |
group Path |
select Name > test.txt
In this example it generates a list of files containing the string acrn164524 the output looks like this:
Name
----
C:\data\logo.eps
C:\data\invoice.docx
C:\data\special.docx
InputStream
C:\datanew\special.docx
I have been using
Get-Content "test.txt" | ForEach-Object {
Copy-Item -Path $_ -Destination "c:\destination\" -Recurse -Container -Force
}
However, this is an issue if two or more files have the same name and also throws a bunch of errors for any lines in the file that are not a path.
sorry if I was not clear enough I would like to keep files with the same name by appending something to the end of the file name.
You seem to want the files, not the output of Select-String. So let's keep the files.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | Select-Object -ExpandProperty FullName | Out-File test.txt
Here
-File will make Get-ChildItem only return actual files. Think
about using a filter like *.txt to reduce the workload more.
-Quiet will make Select-String return $true or $false, which
is perfect for Where-Object.
Instead of Select-Object -ExpandProperty X in order to retrieve an array of raw property values (as opposed to an array of PSObjects, which is what Select-Object would normally do), it's simpler to use ForEach-Object X instead.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | ForEach-Object FullName | Out-File test.txt

How to pipe multiple results into a single command

I've got the following pipeline:
dir -recurse *.* | sls -pattern "matching_pattern" | select -unique path
Which gives me an output like this:
Path
----
D:\code\a.txt
D:\code\b.txt
I want it to call the command gvim a.txt b.txt.
How do I do this?
Use Where-Object instead of Select-String for filtering the files, expand the FullName (or Name) property, so you get an array of paths or filenames, and splat it when calling gvim:
$files = Get-ChildItem -Recurse *.* |
Where-Object { (Get-Content $_.FullName) -match "matching_pattern" } |
Select-Object -Unique -Expand FullName
& gvim #files
Replace FullName with Name in the Select-Object statement to get just the filenames without path.
If you want to stick with Select-String the approach would be similar:
$files = Get-ChildItem -Recurse *.* |
Select-String -Pattern "matching_pattern" |
Select-Object -Unique -Expand Path
& gvim #files
Replace Path with Filename in the Select-Object statement to get just the filenames without path.
You could access the two results by index:
$result = dir -recurse *.* | sls -pattern "matching_pattern" | select -unique path
gvim $result[0].FullName $result[1].FullName

Listing .dll files using powershell?

I am working on a script to list all the files with a specific extension (.dll) in this case. my script is working fine except i want to filter out all of those files which have microsoft's copyright. What approach should be taken ?
$Dir = Get-ChildItem C:\Windows\Microsoft.NET\Framework -include *.dll -recurse | sort-object name | format-table name, directory -auto
$Dir
Filter using $_.VersionInfo.LegalCopyright inside a Where-Object-statement. Ex:
$Dir = Get-ChildItem C:\Windows\Microsoft.NET\Framework -include *.dll -recurse |
Where-Object { $_.VersionInfo.LegalCopyright -notmatch 'Microsoft' }
$Dir | sort-object name | format-table name, directory -auto
Never store data from Format-Table in a variable. It throws away the objects and returns unusable format-objects. Only use it when outputing to console or with ex. | Out-String | Out-File ... when saving to a file.

How to retrieve recursively any files with a specific extensions in PowerShell?

For a specific folder, I need to list all files with extension .js even if nested in subfolders at any level.
The result for the output console should be a list of file names with no extension line by line to be easily copy and pasted in another application.
At the moment I am trying this, but in output console I get several meta information and not a simple list.
Get-ChildItem -Path C:\xx\x-Recurse -File | sort length –Descending
Could you please provide me some hints?
If sorting by Length is not a necessity, you can use the -Name parameter to have Get-ChildItem return just the name, then use [System.IO.Path]::GetFileNameWithoutExtension() to remove the path and extension:
Get-ChildItem -Path .\ -Filter *.js -Recurse -File -Name| ForEach-Object {
[System.IO.Path]::GetFileNameWithoutExtension($_)
}
If sorting by length is desired, drop the -Name parameter and output the BaseName property of each FileInfo object. You can pipe the output (in both examples) to clip, to copy it into the clipboard:
Get-ChildItem -Path .\ -Filter *.js -Recurse -File| Sort-Object Length -Descending | ForEach-Object {
$_.BaseName
} | clip
If you want the full path, but without the extension, substitute $_.BaseName with:
$_.FullName.Remove($_.FullName.Length - $_.Extension.Length)
The simple option is to use the .Name property of the FileInfo item in the pipeline and then remove the extension:
Get-ChildItem -Path "C:\code\" -Filter *.js -r | % { $_.Name.Replace( ".js","") }
There are two methods for filtering files: globbing using an Wildcard, or using a Regular Expression (Regex).
Warning: The globbing method has the drawback that it also matches files which should not be matched, like *.jsx.
# globbing with Wildcard filter
# the error action prevents the output of errors
# (ie. directory requires admin rights and is inaccessible)
Get-ChildItem -Recurse -Filter '*.js' -ErrorAction 'SilentlyContinue'
# filter by Regex
Where-Object { $_.Name -Match '.*\.js$' }
You then can sort by name or filesize as needed:
# sort the output
Sort-Object -PropertyName 'Length'
Format it a simple list of path and filename:
# format output
Format-List -Property ('Path','Name')
To remove the file extension, you can use an select to map the result:
Select-Item { $_.Name.Replace( ".js", "") }
Putting it all together, there is also a very short version, which you should not use in scripts, because it's hardly readable:
ls -r | ? { $_.Name -matches '.*\.js' } | sort Length | % { $_.Name.Replace( ".js", "") | fl
If you like brevity, you can remove the ForEach-Object and quotes. -Path defaults to the current directory so you can omit it
(Get-ChildItem -Filter *.js -Recurse).BaseName | Sort length -Descending
The above Answers works fine. However in WIndows there is a alias called ls the same as on linux so another shorter command that works too would be ls -Filter *.exe
Use BaseName for the file name without the file extension.
Get-ChildItem -Path ".\*.js" | Sort-Object Length -Descending | ForEach-Object {
$_.BaseName
}
I always used cygwin for this in the past. My last employer locked down our environments and it wasn't available. I like to review the latest files I've modified often. I created the following environment variable named LatestCode to store the script. I then execute it with: iex $env:latest code.
Here is the script: get-childitem “.” -recurse -include *.ts, *.html , *.sass, *.java, *.js, *.css | where-object {$_.mode -notmatch “d”} | sort lastwritetime -descending | Select-Object -First 25 | format-table lastwritetime, fullname -autosize