How to search content in files in Powershell - powershell

I want to make a dynamic function that searches for the requested $ErrorCode within the files inputted and eventually copy the files with the error to another folder.
Right now, my code takes only one file and returns the sentence of where the $Error_Code was found. I want to search through multiple files and return the name of the file that have the $ErrorCode.
function SearchError{
Param (
[Parameter (Mandatory=$true)] [STRING] $SourcePath,
[Parameter (Mandatory=$true)] [STRING] $SourceFile,
[Parameter (Mandatory=$true)] [STRING] $ErrorCode,
[Parameter (Mandatory=$true)] [STRING] $FileType
# [Parameter (Mandatory=$true)] [STRING] $DestPath
)
$TargetPath = "$($SourcePath)\$($SourceFile)"
#Return $TargetPath
$DestinationPath = "$($DestPath)"
#Return $DestinationPath
#foreach($error in $TargetPath) {
Get-ChildItem $TargetPath | Select-String -pattern $ErrorCode
}
SearchError

Select-String's output objects - which are of type [Microsoft.PowerShell.Commands.MatchInfo] - have a .Path property that reflects the input file path.
Adding the -List switch to Select-String makes it stop searching after the first match in the file, so you'll get exactly 1 output object for each file in which at least 1 match was found.
Therefore, the following outputs only the paths of the input files in which at least 1 match was found:
Get-ChildItem $TargetPath |
Select-String -List -Pattern $ErrorCode | ForEach-Object Path
Note: -Pattern supports an array of regex patterns, so if you define your $ErrorCode parameter as [string[]], files that have any one of the patterns will match; use -SimpleMatch instead of -Pattern to search by literal substrings instead.
Re:
eventually copy the files with the error to another folder
Simply appending | Copy-Item -Destination $DestPath to the above command should do.
Re:
I want to search through multiple files
Depending on your needs, you can make your $SourcePath and $SourceFile parameters array-valued ([string[]]) and / or pass wildcard expressions as arguments.

Related

Powershell - Copying files with space and variables in the path

I have the script below, I am trying to use copy-item to copy two files. Unfortunately the file path include spaces in them, and I need to use variables to capture the correct files. I have used Test-Path to try and qualify the path, which seem to work, but in the script it is all failures.
$Today = Get-Date -Format "yyyyMMdd"
$Yesterday = (Get-Date).AddDays(-1).ToString('yyyyMMdd')
$folderdate = $Yesterday
$filedate = (Get-Date).AddDays(-1).ToString('dd.MM.yy')
Copy-Item -Path \\posa1251\d$\File_Transfer\NBS\$Today\Daily MI $Yesterday'.xlsx' -Destination \\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\$folderdate\
Copy-Item -Path \\posa1251\d$\File_Transfer\NBS\$Yesterday\$filedate' - West Brom MI.xls' -Destination \\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\$folderdate\
The file paths without the variables are below:
\\posa1251\d$\File_Transfer\NBS\20230201\Daily MI 20230131.xlsx
\\posa1251\d$\File_Transfer\NBS\20230131\31.01.23 - West Brom MI.xls
\\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\20230131\
Any suggestions would be gratefully received.
Essentially, your paths should be wrapped in a expandable string "..." otherwise each value between the spaces are interpreted and bound to other parameters, considering there are available positional parameters available. See about Parameters for more details.
Take the following simple function to understand what's happening:
function Test {
param(
[Parameter(Mandatory)]
[string[]] $Path,
[Parameter(Mandatory)]
[string] $Destination,
[Parameter(ValueFromRemainingArguments)]
[object[]] $RemainingArgs
)
$PSBoundParameters
}
If we try the paths without quotes:
Test -Path \\posa1251\d$\File_Transfer\NBS\$Today\Daily MI $Yesterday'.xlsx' -Destination \\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\$folderdate\
We can see that MI and 20230201.xlsx are being bound to other, remaining in this case, parameters:
Key Value
--- -----
Path {\\posa1251\d$\File_Transfer\NBS\20230202\Daily}
Destination \\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\\
RemainingArgs {MI, 20230201.xlsx}
Instead, if we use quotes:
Test -Path "\\posa1251\d$\File_Transfer\NBS\$Today\Daily MI $Yesterday.xlsx" -Destination "\\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\$folderdate\"
There shouldn't be any problem:
Key Value
--- -----
Path {\\posa1251\d$\File_Transfer\NBS\20230202\Daily MI 20230201.xlsx}
Destination \\wbbsmd.co.uk\corpdata\Corp\Group\NBS_WebSave_Reports\\

PowerShell script to return true or false, takes two input params

I have a requirement,
I have to write a powershell script with two input params, and this script should return true or false.
Parameter1: Path of a text file which contains a list of file names ( ex: a.txt, test.dll etc).
Parameter2: Search directory. (has folders and subfolders)
I have to search for the fileNames listed in the textfile (input parameter1) in the searchDirectory(parameter2).
If any of the fileName exists in any of the (searchDirectory or its folders or subfolders) the script should return 1 else 0.
Please find my below code which is incomplete. Im not sure how to capture GetChildItem result to return 0 or 1.
Please help me with script to achieve the requirement. Thanks in advance.
param(
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
$textFilePath,
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
$searchDirectory
)
foreach($FileName in [System.IO.File]::ReadLines($textFilePath))
{
Get-ChildItem -Path $searchDirectory -Filter $Filename -Recurse
}
If I understand correctly your requirement, you're looking to exit early from your script at first appearance of one of the files, for that you can use the return keyword. The script's body would look like this:
foreach($FileName in [System.IO.File]::ReadLines($textFilePath)) {
# if the file could be found
if(Get-ChildItem -Path $searchDirectory -Filter $Filename -Recurse) {
return 1 # exit early from the script here
}
}
return 0 # return is not needed here
There is also another alternative that might be more efficient, instead of using -Filter you could use -Include which can take an array:
# get an array of all lines in the file, exclude empty lines
$files = #([System.IO.File]::ReadAllLines($textFilePath)) -ne ''
# `Select-Object -First 1` can be used to stop the pipeline at first appeareance of a file
if(Get-ChildItem -Path $searchDirectory -Include $files -Recurse | Select-Object -First 1) {
return 1 # exit early from the script here
}
return 0 # return is not needed here

"Get ChildItem : Cannot find path" showing path I did not supply

I inherited a script that should simply move files from -source to -target. I am prompted for both, and after supplying the paths, it tells me that it cannot find the path while showing a path that I absolutely did not submit, but can't figure out how it's arriving there.
[Parameter(
Mandatory = $true,
Position = 0,
HelpMessage = "Root of the folders or share to archive"
)]
[String] $source,
[Parameter(
Mandatory = $true,
Position = 1,
HelpMessage = "Path of the folder or share of archive"
)]
[string] $target,
[Parameter(
Mandatory = $false,
Position = 3
)]
[int] $days = 30
)
# Get all the files from the source path, that are not shortcuts (*.lnk) and older than the days set
Get-ChildItem $source -Recurse |
Where-Object {!$_.psiscontainer -and ((get-date) - $_.lastwritetime).totaldays -gt $days -and $_.extension -ne ".lnk"} |
ForEach-Object {
# For each file build the destination path
$dest = $_.fullname -replace ([regex]::escape($source)), $target
# Move the files into the destination
Move-Item -Path $_.fullname -Destination $dest -ErrorAction silentlycontinue
}
The log says "Cannot find path '\\appserver\abc$\Automation\Daily\Archive\appserver\abc$\Storage' because it does not exist" - see how it starts repeating itself? \\appserver\abc$\Automation\Daily\Archive\ is the location of the script, whereas \\appserver\abc$\Storage\ is what I am entering as -source. So I have no idea why it is looking at the path to the script, then appending the source path concurrently.
EDIT: This is how I am calling the script (from a little-known finance application called APX):
SHELL PowerShell \\appserver\abc$\Automation\Daily\Archive\ArchiveFiles.ps1 -source \\appserver\abc$\Dataport\dpdata -target \\appserver\abc$\Dataport\archived -days 30
When your script starts, it is beginning in the directory that you are running it from, so it already in \\appserver\abc$\Automation\Daily\Archive\ and if you do not supply a UNC resource prefix such as \\ or A:\ then it will look for ChildItems from that directory down. So when you're supplying the folder path, it's appending that to its current directory and unable to find the new path.
As this would only happen if you had omitted the \\ at the beginning of your string, I would only expect your output if you had submitted appserver\abc$\Storage\ as your source. If you are sure you did supply the \\, then look more closely at whatever line of script is passing the command to this script, to see if there's a reason it's stripping the \\ off beforehand.

Function from imported script doesn't execute

i write a script with a function.
here is the script with the function:
function GenerateHashesForProjects(){
[array]$result=#()
Write-Output "Genrate Hash Values"
$dependencyFolder = Join-Path -Path $PSScriptRoot -ChildPath "..\..\Sources\_Dependencies"
#get all folder in a list below the dependency folder expect the "Modules" folder
$dependencyContent = Get-ChildItem -Path $dependencyFolder | where {$_.PSIsContainer -and ($_.Name -notlike "*Modules*")}
#Fill the result array with the project file name and the depending hash value of this file
foreach ($item in $dependencyContent) {
$denpencyProjects = Get-ChildItem -Path $item.Fullname | where { ($_ -like "*.csproj") }
$hashValue = (Get-FileHash $denpencyProjects.FullName -Algorithm MD5).Hash
$name = $denpencyProjects.Name
Write-Output "name: $name `nvalue: $hashValue"
$result += #($denpencyProjects.Name, $hashValue)
}
return $result
}
That script works fine.
Now i want to use this function also in another script. So i import the script and define a variable with that function. Here is the issue if a call the function without the variable it works fine but with the variable definition not, why?
Here is the second script with the import:
. Join-Path -Path $PSScriptroot -ChildPath "..\..\Build\Tools\GenerateHashesForProjects.ps1"
[array]$dependencyFileValues = GenerateHashesForProjects
This test works fine:
. Join-Path -Path $PSScriptroot -ChildPath "..\..\Build\Tools\GenerateHashesForProjects.ps1"
GenerateHashesForProjects
since you didn't post any responses to questions [grin], here is one way to rewrite your code.
what it does ...
creates an advanced function
uses the recommended name format for such
does not supply the "otta be there" Comment Based Help [grin]
defines the parameters
only the $Path is required.
defines but does not use a begin {} block
defines a process {} block
grabs a list of the dirs that branch from the source path
filters out the dirs that are in the $ExcludeDirList
gets the files in those dirs that match the $FileFilter
iterates thru that list
builds a [PSCustomObject] for each file with the desired details
you can add or remove them as needed.
sends that PSCO out to the calling code
the line that calls the function stores the entire set of results into the $Result variable and then shows that on screen.
a few notes ...
i had to change a lot of your details since i have no csproj files
there are no "what is happening" lines
if you need that, you can easily add such. i would NOT use Write-Output, tho, since that will pollute your output data.
there is no error detection OR error handling
here's the code ...
function Get-ProjectFileHash
{
<#
CommentBasedHelp goes here
#>
[CmdletBinding ()]
Param
(
[Parameter (
Mandatory,
Position = 0
)]
[string]
$Path,
[Parameter (
Position = 1
)]
[ValidateSet (
'MD5',
'MACTripleDES',
'RIPEMD160',
'SHA1',
'SHA256',
'SHA384',
'SHA512'
)]
[string]
$Algorithm = 'MD5',
[Parameter (
Position = 2
)]
[string[]]
$ExcludeDirList,
[Parameter (
Position = 3
)]
[string]
$FileFilter
)
begin {}
process
{
$ProjDirList = Get-ChildItem -LiteralPath $Path -Directory |
Where-Object {
# the "-Exclude" parameter of G-CI is wildly unreliable
# this avoids that problem [*grin*]
# build a regex OR listing to exclude
$_.Name -notmatch ($ExcludeDirList -join '|')
}
$FileList = Get-ChildItem -LiteralPath $ProjDirList.FullName -File -Filter $FileFilter
foreach ($FL_Item in $FileList)
{
[PSCustomObject]#{
FileName = $FL_Item.Name
DirName = $FL_Item.Directory
Algorithm = $Algorithm
Hash = (Get-FileHash -LiteralPath $FL_Item.FullName -Algorithm $Algorithm).Hash
}
}
}
end {}
} # end >>> function Get-ProjectFileHash
$Source = 'C:\ProgramData\chocolatey\lib'
$NotWanted = 'choco', '7zip', 'kb', 'bad', 'bkp'
$Filter = '*.nupkg'
$Result = Get-ProjectFileHash -Path $Source -Algorithm MD5 -ExcludeDirList $NotWanted -FileFilter $Wanted
$Result
truncated output ...
FileName DirName Algorithm Hash
-------- ------- --------- ----
autohotkey.nupkg C:\ProgramData\chocolatey\lib\autohotkey MD5 35A1B894AEA7D3473F3BBCBF5788D2D6
autohotkey.install.nupkg C:\ProgramData\chocolatey\lib\autohotkey.install MD5 EFE8AD812CBF647CFA116513AAD4CC15
autohotkey.portable.nupkg C:\ProgramData\chocolatey\lib\autohotkey.portable MD5 D31FA1B5496AAE266E4B0545835E9B19
[*...snip...*]
vcredist2015.nupkg C:\ProgramData\chocolatey\lib\vcredist2015 MD5 56321731BC0AEFCA3EE5E547A7A25D5E
vlc.nupkg C:\ProgramData\chocolatey\lib\vlc MD5 8177E24675461BDFF33639BF1D89784B
wiztree.nupkg

Powershell variable contains the value twice

I have a powershell script that is started bij a jenskinsfile. This all works well. But in the script I have a function to download a file. This does not work, because the $filePath variable contains the value twice. When this part would be run the log would look like:
https://example.com/api/download
D:\folder\file_2.txt D:\folder\file_2.txt
How can I get the value only to be there ones in $filePath ?
function DownloadFile ($folder, $version) {
$wc = New-Object System.Net.WebClient
$wc.Headers['Content-Type'] = 'application/json'
$requesturl = "https://example.com/api/download"
$filePath = Join-Path -Path $folder -ChildPath "\file_$version.txt"
Write-Host "$requesturl"
Write-Host "$filePath"
$wc.DownloadFile($requesturl, $filePath)
return $filePath
}
The implication is that the argument you're passing to the -folder parameter (as represented inside your function as the $folder parameter variable) is an array of folder paths, not a single one.
The solution is therefore to make sure that you only pass a single folder path when you call your DownloadFile function; e.g.:
DownloadFile -folder D:\folder -version 2
# With *positional* parameter binding:
DownloadFile D:\folder 2
Since Join-Path accepts an array of paths as a -Path argument, it outputs multiple paths when given an array; e.g.:
PS> $folder = 'c:\abc', 'c:\def'; Join-Path -Path $folder -ChildPath file.txt
c:\abc\file.txt
c:\def\file.txt
Passing an array Write-Host implicitly stringifies it, which means creating a single string composed of the array elements joined with spaces:
PS> $folder = 'c:\abc', 'c:\def'; Write-Host $folder
c:\abc c:\def
(Note that this differs from implicit output / output via Write-Output, which prints each array element on its own line; also, implicit output / Write-Output write to the pipeline, meaning they output data for later processing, whereas Write-Host writes strings to the display).