itsource

PowerShell에서 일부 파일 및 폴더를 제외한 재귀 디렉터리 및 파일 목록을 검색하는 방법은 무엇입니까?

mycopycode 2023. 8. 30. 21:41
반응형

PowerShell에서 일부 파일 및 폴더를 제외한 재귀 디렉터리 및 파일 목록을 검색하는 방법은 무엇입니까?

파일은 " " " " " " " " " " " " " " " "*.log,그리고.myFile.txt및 )을 합니다.myDir 의모파아및폴더 아래의 및 myDir).

CmdLet 및 CmdLet로 작업을 해왔지만 정확한 동작을 알 수 없습니다.

저는 키스 힐의 대답이 두 단계를 넘어 재발하는 것을 막는 버그가 있다는 것을 제외하고는 좋습니다.다음 명령은 버그를 나타냅니다.

New-Item level1/level2/level3/level4/foobar.txt -Force -ItemType file
cd level1
GetFiles . xyz | % { $_.fullname }

힐의 원래 코드를 사용하면 다음과 같이 얻을 수 있습니다.

...\level1\level2
...\level1\level2\level3

다음은 수정되고 약간 리팩터된 버전입니다.

function GetFiles($path = $pwd, [string[]]$exclude)
{
    foreach ($item in Get-ChildItem $path)
    {
        if ($exclude | Where {$item -like $_}) { continue }

        $item
        if (Test-Path $item.FullName -PathType Container)
        {
            GetFiles $item.FullName $exclude
        }
    }
} 

버그 수정을 시행하면 다음과 같은 수정된 출력이 표시됩니다.

...\level1\level2
...\level1\level2\level3
...\level1\level2\level3\level4
...\level1\level2\level3\level4\foobar.txt

는 또한 간결함에 대한 jajk의 대답을 좋아합니다, 그가 지적했듯이, 그것은 덜 효율적입니다.그나저나, 이것이 덜 효율적인 이유는 jk가 계속되는 동안 제거 대상을 찾을 때 힐의 알고리즘이 하위 트리를 통과하는 것을 멈추기 때문입니다.하지만 잭의 대답은 또한 제가 조상의 덫이라고 부르는 결함으로 고통 받고 있습니다.동일한 경로 구성요소(즉, subdir2)를 두 번 포함하는 이와 같은 경로를 고려합니다.

\usr\testdir\subdir2\child\grandchild\subdir2\doc

예를 들어, 중간에 위치를 설정합니다.cd \usr\testdir\subdir2\child그런 다음 잭의 알고리즘을 실행하여 아래쪽을 필터링합니다.subdir2그리고 당신은 전혀 출력을 얻지 못할 것입니다. 즉, 그것은 존재하기 때문에 모든 것을 걸러냅니다.subdir2에 이 한문제로 입니다.하지만 이것은 궁지에 몰린 사건이고 자주 타격을 받을 가능성은 낮으므로, 저는 이 한 가지 문제로 인해 잭의 해결책을 배제하지 않을 것입니다.

그럼에도 불구하고 위의 두 가지 버그 중 하나가 없는번째 대안을 제시합니다.제거할 경로에 대한 편의성 정의가 포함된 기본 알고리즘은 다음과 같습니다. 수정만 하면 됩니다.$excludeList사용할 대상 집합으로 이동합니다.

$excludeList = @("stuff","bin","obj*")
Get-ChildItem -Recurse | % {
    $pathParts = $_.FullName.substring($pwd.path.Length + 1).split("\");
    if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $_ }
}

나의 알고리즘은 합리적으로 간결하지만, jajk의 알고리즘과 마찬가지로 Hill의 알고리즘보다 덜 효율적입니다(같은 이유로: 가지치기 대상에서 하위 트리를 통과하는 것을 멈추지 않습니다).하지만 내 코드는 힐의 코드보다 중요한 이점을 가지고 있습니다. 파이프라인을 사용할 수 있습니다!따라서 필터 체인에 끼워 맞춤형 Get-ChildItem을 만들 수 있지만 Hill의 재귀 알고리즘은 자체적인 결함이 없습니다.ajk의 알고리즘은 파이프라인 사용에도 적용될 수 있지만 제외할 항목을 지정하는 것은 제가 사용한 단순한 항목 목록이 아닌 정규식에 포함되어 있기 때문에 깨끗하지 않습니다.

트리 가지치기 코드를 Get-ChildItem의 향상된 버전으로 패키징했습니다.제 다소 상상력이 부족한 이름인 Get-Enhanced ChildItem을 제외하고, 저는 그것에 대해 신이 나서 제 오픈 소스 Powershell 라이브러리에 포함시켰습니다.트리 가지치기 외에도 몇 가지 새로운 기능이 포함되어 있습니다.또한 코드는 확장 가능하도록 설계되어 있습니다. 새로운 필터링 기능을 추가하려면 쉽게 할 수 있습니다.기본적으로 Get-ChildItem은 먼저 호출되고 명령 매개 변수를 통해 활성화하는 각 연속 필터에 파이프라인으로 연결됩니다.그러니까 이런 것들이...

Get-EnhancedChildItem –Recurse –Force –Svn
    –Exclude *.txt –ExcludeTree doc*,man -FullName -Verbose 

내부적으로 다음으로 변환됩니다.

Get-ChildItem | FilterExcludeTree | FilterSvn | FilterFullName

각 필터는 특정 규칙(FileInfo 및 Directory 허용)을 준수해야 합니다.객체를 입력으로 정보를 제공하고 출력과 동일하게 생성하며 파이프라인에 삽입할 수 있도록 stdin 및 stdout을 사용합니다.다음은 이러한 규칙에 맞게 리팩터링된 동일한 코드입니다.

filter FilterExcludeTree()
{
  $target = $_
  Coalesce-Args $Path "." | % {
    $canonicalPath = (Get-Item $_).FullName
    if ($target.FullName.StartsWith($canonicalPath)) {
      $pathParts = $target.FullName.substring($canonicalPath.Length + 1).split("\");
      if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $target }
    }
  }
} 

여기서 유일하게 추가된 부분은 Coalesce-Args 함수(이 게시물에서 Keith Dahlby가 발견)로, 호출이 경로를 지정하지 않은 경우 현재 디렉터리를 파이프 아래로 보내기만 합니다.

이 답변은 이 필터에 대해 더 자세히 설명하기보다는 다소 길어지기 때문에 관심 있는 독자들에게 최근 Simple-Talk.com 에 발표된 "실용 PowerShell: 파일 트리 가지치기 및 확장 Cmdlets"라는 제목의 기사를 참조하여 Get-Enhanced ChildItem에 대해 훨씬 더 자세히 설명합니다.하지만 마지막으로 언급할 것은 제 오픈 소스 라이브러리인 New-FileTree의 또 다른 기능입니다. 이 기능은 테스트 목적으로 더미 파일 트리를 생성하여 위의 알고리즘 중 하나를 실행할 수 있도록 합니다.그리고 당신이 이것들 중 하나를 실험할 때, 저는 배관을 추천합니다.% { $_.fullname }첫 번째 코드 조각에서 했던 것처럼 더 유용한 출력을 검사할 수 있습니다.

에는 Get-ChildItem cmdlet이라는 파일이 .-Exclude사용하고 싶지만 내가 알 수 있는 것에서 전체 디렉터리를 걸러내는 데는 작동하지 않는 매개 변수입니다.다음과 같은 방법을 사용해 보십시오.

함수 GetFiles($path = $pwd, [string[]$files]){각($Get-ChildItem $path의 항목)에 대해{if ($exclude | 여기서 {$item - like $_} { continue }
if(Test-Path $항목).전체 이름 - 경로 유형 컨테이너){$항목파일 가져오기 $항목.전체 이름 $제외}또 다른{$항목}}}

효율성은 떨어지지만 보다 간결한 다른 옵션이 있습니다.이것이 제가 일반적으로 이런 종류의 문제를 처리하는 방법입니다.

Get-ChildItem -Recurse .\targetdir -Exclude *.log |
  Where-Object { $_.FullName -notmatch '\\excludedir($|\\)' }

\\excludedir($|\\)'expression을 사용하면 디렉토리와 해당 내용을 동시에 제외할 수 있습니다.

업데이트: 이 접근법으로 엣지 케이스 결함에 대한 msorens의 훌륭한 답변과 전반적으로 훨씬 더 살이 찐 솔루션을 확인해 주십시오.

최근에, 저는 스캔할 폴더와 재귀 스캔 결과가 저장될 위치를 매개 변수화할 수 있는 가능성을 탐구했습니다.마지막으로 스캔한 폴더 수와 내부 파일 수도 요약했습니다.다른 개발자에게 도움이 될 경우를 대비하여 커뮤니티와 공유합니다.

    ##Script Starts
    #read folder to scan and file location to be placed

    $whichFolder = Read-Host -Prompt 'Which folder to Scan?'  
    $whereToPlaceReport = Read-Host -Prompt 'Where to place Report'
    $totalFolders = 1
    $totalFiles = 0

    Write-Host "Process started..."

    #IMP separator ? : used as a file in window cannot contain this special character in the file name

    #Get Foldernames into Variable for ForEach Loop
    $DFSFolders = get-childitem -path $whichFolder | where-object {$_.Psiscontainer -eq "True"} |select-object name ,fullName

    #Below Logic for Main Folder
    $mainFiles = get-childitem -path "C:\Users\User\Desktop" -file
    ("Folder Path" + "?" + "Folder Name" + "?" + "File Name " + "?"+ "File Length" )| out-file "$whereToPlaceReport\Report.csv" -Append

    #Loop through folders in main Directory
    foreach($file in $mainFiles)
    {

    $totalFiles = $totalFiles + 1
    ("C:\Users\User\Desktop" + "?" + "Main Folder" + "?"+ $file.name + "?" + $file.length ) | out-file "$whereToPlaceReport\Report.csv" -Append
    }


    foreach ($DFSfolder in $DFSfolders)
    {
    #write the folder name in begining
    $totalFolders = $totalFolders + 1

    write-host " Reading folder C:\Users\User\Desktop\$($DFSfolder.name)"
    #$DFSfolder.fullName | out-file "C:\Users\User\Desktop\PoC powershell\ok2.csv" -Append
    #For Each Folder obtain objects in a specified directory, recurse then filter for .sft file type, obtain the filename, then group, sort and eventually show the file name and total incidences of it.

    $files = get-childitem -path "$whichFolder\$($DFSfolder.name)" -recurse

    foreach($file in $files)
    {
    $totalFiles = $totalFiles + 1
    ($DFSfolder.fullName + "?" + $DFSfolder.name + "?"+ $file.name + "?" + $file.length ) | out-file "$whereToPlaceReport\Report.csv" -Append
    }

    }


    # If running in the console, wait for input before closing.
    if ($Host.Name -eq "ConsoleHost")
    {

    Write-Host "" 
    Write-Host ""
    Write-Host ""

    Write-Host  "                            **Summary**"  -ForegroundColor Red
    Write-Host  "                            ------------" -ForegroundColor Red

    Write-Host  "                           Total Folders Scanned = $totalFolders "  -ForegroundColor Green
    Write-Host  "                           Total Files   Scanned = $totalFiles "     -ForegroundColor Green

    Write-Host "" 
    Write-Host "" 
        Write-Host "I have done my Job,Press any key to exit" -ForegroundColor white
        $Host.UI.RawUI.FlushInputBuffer()   # Make sure buffered input doesn't "press a key" and skip the ReadKey().
        $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
    }

##Output

enter image description here

##Bat Code to run above powershell command

@ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%PowerShellScriptPath%""' -Verb RunAs}";

조금 늦었지만, 이것을 시도해 보세요.

function Set-Files($Path) {
    if(Test-Path $Path -PathType Leaf) {
        # Do any logic on file
        Write-Host $Path
        return
    }

    if(Test-Path $path -PathType Container) {
        # Do any logic on folder use exclude on get-childitem
        # cycle again
        Get-ChildItem -Path $path | foreach { Set-Files -Path $_.FullName }
    }
}

# call
Set-Files -Path 'D:\myFolder'

파워셸에서 특정 디렉터리를 제외한 상태에서 파일을 검색하는 주제에 대해 여기에 주석을 다는 것이 가장 인기 있는 답변인 것 같습니다.

결과의 사후 필터링과 관련된 문제(예: 권한 문제 방지 등)를 방지하기 위해 최상위 디렉터리만 필터링하면 됩니다. 이 예제는 모두 하위 디렉터리 이름을 필터링하지 않지만, 원하는 경우 이를 지원하기 위해 매우 쉽게 재귀적으로 만들 수 있습니다.

스니펫 작동 방식에 대한 빠른 분석

$folders << Get-Child 항목을 사용하여 파일 시스템을 쿼리하고 폴더 제외를 수행합니다.

$file << 내가 찾고 있는 파일의 패턴

각 << Get-Childitem 명령을 사용하여 재귀 검색을 수행하는 $folders 변수를 반복합니다.

$folders = Get-ChildItem -Path C:\ -Directory -Name -Exclude Folder1,"Folder 2"
$file = "*filenametosearchfor*.extension"

foreach ($folder in $folders) {
   Get-Childitem -Path "C:/$folder" -Recurse -Filter $file | ForEach-Object { Write-Output $_.FullName }
}

언급URL : https://stackoverflow.com/questions/8024103/how-to-retrieve-a-recursive-directory-and-file-list-from-powershell-excluding-so

반응형