简体   繁体   中英

PowerShell read text file line by line and find missing file in folders

I am a novice looking for some assistance. I have a text file containing two columns of data. One column is the Vendor and one is the Invoice. I need to scan that text file, line by line, and see if there is a match on Vendor and Invoice in a path. In the path, $Location, the first wildcard is the Vendor number and the second wildcard is the Invoice I want the non-matches output to a text file.

$Location = "I:\\Vendors\*\Invoices\*"
$txt = "C:\\Users\sbagford.RECOEQUIP\Desktop\AP.txt"
$Output ="I:\\Vendors\Missing\Missing.txt"
foreach ($line in Get-Content $txt) {
if (-not($line -match $location)){$line}
}
set-content $Output -value $Line

Sample Data from txt or csv file.

kvendnum    wapinvoice
000953  90269211
000953  90238674
001072  11012016
002317  448668
002419  06123711
002419  06137343
002419  06134382
002419  759208
002419  753087
002419  753069
002419  762614
003138  N6009348
003138  N6009552
003138  N6009569
003138  N6009612
003182  770016
003182  768995
003182  06133429

In above data the only match is on the second line: 000953 90238674 and the 6th line: 002419 06137343

Untested, but here's how I'd approach it:

$Location = "I:\\Vendors\\.+\\Invoices\\.+"
$txt = "C:\\Users\sbagford.RECOEQUIP\Desktop\AP.txt"
$Output ="I:\\Vendors\Missing\Missing.txt"
select-string -path $txt -pattern $Location -notMatch |
    set-content $Output

There's no need to pick through the file line-by-line; PowerShell can do this for you using select-string . The -notMatch parameter simply inverts the search and sends through any lines that don't match the pattern.

select-string sends out a stream of matchinfo objects that contain the lines that met the search conditions. These objects actually contain far more information that just the matching line, but fortunately PowerShell is smart enough to know how to send the relevant item through to set-content .

Regular expressions can be tricky to get right, but are worth getting your head around if you're going to do tasks like this.

EDIT

$Location  = "I:\Vendors\{0}\Invoices\{1}.pdf"
$txt = "C:\\Users\sbagford.RECOEQUIP\Desktop\AP.txt"
$Output  = "I:\Vendors\Missing\Missing.txt"

get-content -path $txt | 
    % {

        # extract fields from the line
        $lineItems = $_ -split "  "

        # construct path based on fields from the line
        $testPath = $Location -f $lineItems[0], $lineItems[1]

        # for debugging purposes
        write-host ( "Line:'{0}'  Path:'{1}'" -f $_, $testPath )

        # test for existence of the path; ignore errors
        if ( -not ( get-item -path $testPath -ErrorAction SilentlyContinue ) ) {
            # path does not exist, so write the line to pipeline
            write-output $_ 

        }

    } |
    Set-Content -Path $Output

I guess we will have to pick through the file line-by-line after all. If there is a more idiomatic way to do this, it eludes me.

Code above assumes a consistent format in the input file, and uses -split to break the line into an array.

EDIT - version 3

$Location  = "I:\Vendors\{0}\Invoices\{1}.pdf"
$txt = "C:\\Users\sbagford.RECOEQUIP\Desktop\AP.txt"
$Output  = "I:\Vendors\Missing\Missing.txt"

get-content -path $txt | 
    select-string "(\S+)\s+(\S+)" | 
    %{

        # pull vendor and invoice numbers from matchinfo     
        $vendor = $_.matches[0].groups[1]
        $invoice = $_.matches[0].groups[2]

        # construct path
        $testPath = $Location -f $vendor, $invoice

        # for debugging purposes
        write-host ( "Line:'{0}'  Path:'{1}'" -f $_.line, $testPath )

        # test for existence of the path; ignore errors
        if ( -not ( get-item -path $testPath -ErrorAction SilentlyContinue ) ) {
            # path does not exist, so write the line to pipeline
            write-output $_ 
        }

    } |
    Set-Content -Path $Output

It seemed that the -split " " behaved differently in a running script to how it behaves on the command line. Weird. Anyway, this version uses a regular expression to parse the input line. I tested it against the example data in the original post and it seemed to work.

The regex is broken down as follows

(     Start the first matching group
\S+   Greedily match one or more non-white-space characters
)     End the first matching group
\s+   Greedily match one or more white-space characters
(     Start the second matching group
\S+   Greedily match one or more non-white-space characters
)     End the second matching groups

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM