Where-Object中的多个条件运算符

时间:2015-02-27 15:09:53

标签: powershell csv

#foo.csv
Month, Name, First, Second, Third, Status
2015/01, 'Google', 02-02-2014, 03-02-2014, 03-02-2015, "Lost"
2015/01, 'Google', 03-01-2014,  03-01-2014, 03-02-2015, "Active"
2015/02, 'Google', 06-02-2013,  03-01-2014, N/A, "Lost"
2015/02, 'Yahoo', 07-02-2013,  06-01-2015, 03-02-2015, "Active"
2015/02, 'Google', 07-02-2013,  06-01-2015, 03-02-2015, "Lost" 
2015/02, 'Yahoo', 03-01-2014,  06-01-2015, N/A, "Active"
2015/02, 'Google', 06-01-2015,  06-01-2015, 03-02-2015, "Lost" 
2014/12, 'Yahoo', 03-01-2014,  06-01-2015, 03-02-2014, "Active"
2014/12, 'Google', 03-05-2014,  06-01-2015, N/A, "Active" 
2014/12, 'Yahoo', 06-01-2015,  06-01-2015, 03-02-2014, "Active"

Import-Csv "E:\foo.csv"  |
Where-Object {($_."Name" -eq "Google") `
-and ($_."Third" -gt (get-date).AddDays(-27).ToString("yyy-MM-dd") -or $_."C3 Date" -eq "N/A") `
-and # where month is last two months while Status is only Active ` 
-and # no current months (Feb) data/values of last years from First and Second column }

注意:
月份列值采用字符串格式。 (那里没问题)。其余的日期格式为dd-mm-yyyy(例如23-12-2014)

我试图从上面的csv文件中只选择所需的记录,选择步骤如下:

  1. 首先只选择Google记录。
  2. 然后仅选择第三列日期为当前日期的记录 月或不适用。
  3. 然后只记录Month值等于最近两个月的记录 并且同时状态应该是活动的。
  4. 最后,跳过当前月份(2月)值的记录 过去的一年(例如2014 / 02,2013 / 02,但不是2015/02)来自First和 第二栏。
  5. 我能写脚本,直到2步。输出匹配所需的数量 3和4步包含多个if条件/逻辑,我无法编写和理解。

    注意: foo.csv中的第2和第9条记录是我们正在寻找的确切记录,满足所有条件。

    修改 第9次记录更正 当我尝试使用Matt的代码时,我收到了以下错误:

    The operation '[System.Int32] - [System.DateTime]' is not defined.
    At line:8 char:29
    

    在我的文化信息中,我可以看到en-GB,但我希望它独立于文化。以前它是逻辑部分,我正在努力,但现在它的日期(格式和匹配)问题 我正在阅读过去2天的日期和时间格式。我尝试将yyyy改为yy或yyy,即使这会产生错误。仍然试图在有关日期问题的第二个条件下做到正确,但还没有运气。

    编辑2:
    下面是POC代码,它只做了一些部分,但是我不能在这里控制日期格式,因为将来数据可能会在csv dd-mm-yyyy中改变为mm-dd-yy或其他内容。

    Import-Csv E:\foo.csv | 
    Select-Object @{Label="Third"; Expression={[datetime]$_.Third}} |
    Where-Object { $_.Third -gt [datetime]'01-03-2015' } |
    Format-Table -Auto
    

    此外,在将日期转换为日期时间格式时,是否有可能保留N / A?

2 个答案:

答案 0 :(得分:2)

您最好将所有这些步骤拆分为单独的Where-Object,否则构建和调试此类语句将非常困难。这就是我所做的。问题是,当您说记录#2 #9 是您要查找的记录时,记录#9 会失败你的规则#3

  

然后只记录Month值等于最近两个月的记录   同时状态应为Active

记录是:

  

2014/12,'Google',03-05-2014,06-01-2015,N / A,“Lost”

并且Status设置为Lost。因此,以下代码只显示#2 记录:

编辑:这是一个非常详细的脚本,它将处理您的CSV。将其另存为ProcessCSV.ps1并按以下方式运行:.\ProcessCSV -Path 'E:\foo.csv' -Verbose。它应该显示很多的调试数据,这将有助于诊断出现了什么问题。例如:

VERBOSE: Importing CSV's
VERBOSE: ==============================[ Processing record #1 ]==============================
VERBOSE: [Selecting only Google records]
VERBOSE: Name: Google
VERBOSE: Found Google record, returning: True
VERBOSE: [Selecting only those records where Third column date is of current month (3) in this year (2015) or N/A]
VERBOSE: Third column value is: 03/03/2015 00:00:00
VERBOSE: Third column value converted to date is: 3 марта 2015 г.
VERBOSE: Third column date is in current year (2015): 2015
VERBOSE: Third column date is in current month (3): 3, returning: True
VERBOSE: [Selecting records where Month value equals last two months and Status is Active]
VERBOSE: Status column value is: Lost
VERBOSE: Status column value is not equal to: Active, returning False

脚本强制每列的自定义日期格式,您可以在将来自行修改它。在$DateFmt函数的Begin块中查找Select-Data变量:

$DateFmt = @{
    # The year as a four-digit number
    # The month, from 01 through 12
    Month = 'yyyy/MM'
...

凭借自定义Select-Object的想法,对[DateTime] Matt 感到荣幸,这样做效果更好!

ProcessCSV.ps1脚本:

[CmdLetBinding()]
Param
(
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({
        if(!(Test-Path -LiteralPath $_ -PathType Leaf))
        {
            throw "File doesn't exist: $_"
        }
        $true
    })]
    [ValidateNotNullOrEmpty()]
    [string[]]$Path
)

function Select-Data
{
    [CmdLetBinding()]
    Param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        $Data
    )

    Begin
    {
        # Setup custom date formatting rules for input data
        # https://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
        $DateFmt = @{
            # The year as a four-digit number
            # The month, from 01 through 12
            Month = 'yyyy/MM'

            # The day of the month, from 01 through 31
            # The month, from 01 through 12
            # The year as a four-digit number,
            First = 'dd-MM-yyyy'

            # The day of the month, from 01 through 31
            # The month, from 01 through 12
            # The year as a four-digit number,
            Second = 'dd-MM-yyyy'

            # The day of the month, from 01 through 31
            # The month, from 01 through 12
            # The year as a four-digit number,
            Third = 'dd-MM-yyyy'
        }

        # Setup custom names
        $Source = @{
            Google = 'Google'
            Yahoo = 'Yahoo'
        }

        # Setup custom statuses
        $Status = @{
            Lost = 'Lost'
            Active = 'Active'
        }

        $Misc = @{
            NA = 'N/A'
            Culture = ([System.Globalization.CultureInfo]::InvariantCulture)
        }
    }

    Process
    {
        $Data |
            Select-Object @{Label = 'Month' ; Expression = {if($_.Month -eq $Misc.NA){$_.Month}else{[DateTime]::ParseExact($_.Month, $DateFmt.Month, $Misc.Culture)}}},
                @{Label = 'Name' ; Expression = {$_.Name.Trim("'")}},
                @{Label = 'First' ; Expression = {if($_.First -eq $Misc.NA){$_.First}else{[DateTime]::ParseExact($_.First, $DateFmt.First, $Misc.Culture)}}}, 
                @{Label = 'Second' ; Expression = {if($_.Second -eq $Misc.NA){$_.Second}else{[DateTime]::ParseExact($_.Second, $DateFmt.Second, $Misc.Culture)}}}, 
                @{Label = 'Third' ; Expression = {if($_.Third -eq $Misc.NA){$_.Third}else{[DateTime]::ParseExact($_.Third, $DateFmt.Third, $Misc.Culture)}}}, 
                Status |
            ForEach-Object {
                $i++
                Write-Verbose ('=' * 30 + "[ Processing record #$i ]" + '=' * 30)
                $_
            } |
            Where-Object {
                Write-Verbose "[Selecting only $($Source.Google) records]"
                Write-Verbose "Name: $($_.Name)"
                if($_.Name -eq $Source.Google)
                {
                    Write-Verbose "Found $($Source.Google) record, returning: $true"
                    return $true
                }
                else
                {
                    Write-Verbose "Not found $($Source.Google) record, returning: $false"
                    return $false
                }
            } |
            Where-Object {
                Write-Verbose "[Selecting only those records where Third column date is of current month ($((Get-Date).Month)) in this year ($((Get-Date).Year)) or $($Misc.NA)]"
                Write-Verbose "Third column value is: $($_.Third)"
                if($_.Third -eq $Misc.NA)
                {
                    Write-Verbose "Third column value is equal to: $($Misc.NA), returning: $true"
                    return $true
                }
                else
                {
                    Write-Verbose "Third column value converted to date is: $($_.Third.ToLongDateString())"
                    if($_.Third.Year -eq (Get-Date).Year)
                    {
                        Write-Verbose "Third column date is in current year ($((Get-Date).Year)): $($_.Third.Year)"
                        if($_.Third.Month -eq (Get-Date).Month)
                        {
                            Write-Verbose "Third column date is in current month ($((Get-Date).Month)): $($_.Third.Month), returning: $true"
                            return $true
                        }
                        else
                        {
                            Write-Verbose "Third column date is not in current month ($((Get-Date).Month)): $($_.Third.Month), returning: $false"
                            return $false
                        }
                    }
                    else
                    {
                         Write-Verbose "Third column date is not in current year ($((Get-Date).Year)): $($_.Third.Year), returning: $false"
                         return $false
                    }
                }
            } |
            Where-Object {
                Write-Verbose '[Selecting records where Month value equals last two months and Status is Active]'
                Write-Verbose "Status column value is: $($_.Status)"
                if($_.Status -eq $Status.Active)
                {
                    Write-Verbose "Status column value is equal to: $($Status.Active)"
                    if($_.Month -ge (Get-Date).AddMonths(-2))
                    {
                        Write-Verbose "Month column date is: $($_.Month.ToLongDateString()), looks like it less than 2 months old, returning $true"
                        return $true
                    }
                    else
                    {
                        Write-Verbose "Month column date is: $($_.Month.ToLongDateString()), looks like it more than 2 months old, returning $false"
                        return $false
                    }
                }
                else
                {
                    Write-Verbose "Status column value is not equal to: $($Status.Active), returning $false"
                    return $false
                }
            } |
            Where-Object {
                Write-Verbose '[Skip records for current month from past year in First and Second columns]'
                Write-Verbose "First column date is: $($_.First.ToLongDateString())"
                Write-Verbose "Second column date is: $($_.Second.ToLongDateString())"

                $ret = $_.First, $_.Second |
                    Where-Object {
                        if($_.Month -eq (Get-Date).Month)
                        {
                            Write-Verbose "Month of date $($_) is equal to current month: $((Get-Date).Month)"
                            if($_.Year -eq (Get-Date).Year)
                            {
                                Write-Verbose "Year of date $($_.ToLongDateString()) is equal to current year: $((Get-Date).Year), returning $true"
                                return $true
                            }
                            else
                            {
                                Write-Verbose "Year of date $($_.ToLongDateString()) is not equal to current year: $((Get-Date).Year), returning $false"
                                return $false
                            }
                        }
                        else
                        {
                            Write-Verbose "Month of date $($_.ToLongDateString()) is not equal to current month: $((Get-Date).Month), returning $true"
                            return $true
                        }
                    }
                if([array]$ret.Count -eq 2)
                {
                    Write-Verbose "Both First and Second columns are not in current month from past years"
                    return $true
                }
                else
                {
                    Write-Verbose "First or Second columns is in current month ($((Get-Date).Year)) from past year, returning $false"
                    return $false
                }
            }
    }
}

# Import data and process data
Write-Verbose 'Importing CSV''s'
$Path |
    Import-Csv |
        Select-Data
Write-Verbose 'All done'

答案 1 :(得分:1)

所以要测试的许多条件都不存在于样本数据中,但这可以在一个where子句中完成,该子句不难阅读或理解多个where子句。

我同意beatcracker的意见,因为你的预期结果不正确,因为第9条记录不是"活跃"

$culture = [System.Globalization.CultureInfo]::InvariantCulture
$today = Get-Date "02-27-2015"
Import-CSV c:\temp\foo.csv | Select-Object @{Label="Month";Expression={[datetime]$_.Month}},
        Name, @{Label="First";Expression={[datetime]::ParseExact($_.First,"dd-MM-yyyy",$culture)}},
        @{Label="Second";Expression={[datetime]::ParseExact($_.Second,"dd-MM-yyyy",$culture)}},
        @{Label="Third";Expression={[datetime]::ParseExact($_.Third,"dd-MM-yyyy",$culture)}},
        Status | 
    Where-Object {
        ($_.Name -eq "'Google'") -and      # Condition 1
        (!$_.Third -or (($_.Third).Month -eq $today.Month)) -and # Condition 2
        (($today.Month - $_.Month + (($today.Year - $_.Year) * 12)) -lt 3) -and # Condition 3.1
        ($_.Status -eq "Active") -and # Condition 3.2
        (!((($_.First).Month -eq $today.Month) -and (($_.First).Year -ne $today.Year))) -and # Condition 4.1
        (!((($_.Second).Month -eq $today.Month) -and (($_.Second).Year -ne $today.Year))) # Condition 4.2
    } | Select-Object @{L="Month";E={($_.Month).ToString("yyyy/MM")}},Name,
            @{L="First";E={($_.First).ToString("dd-MM-yyyy")}},
            @{L="Second";E={($_.Second).ToString("dd-MM-yyyy")}},
            @{L="Last";E={If($_.Third){($_.Third).ToString("dd-MM-yyyy")}Else{"N/A"}}},Status |
    Export-CSV "c:\temp\outputfile.csv" -NoTypeInformation

您遇到的一个较大问题是您在字符串而不是日期对象上进行日期算术。使用Import-CSV导入数据时,它会转换为字符串。为了解决这个问题,我们运行Select并使用计算表达式将数据转换为[datetime]个对象。

为了帮助理解Where条款,我在最后有评论,因此您知道哪个条款旨在解决您的哪些条件。一对夫妇分手了更多的条款来帮助提高可读性。

还有一个问题是我不知道你的数据时间格式是第一,第二和第三。我以为它是" dd-MM-yyyy"根据您的假设。如果这里有问题,请告诉我

根据您的输出需求,您需要格式化字符串以匹配您想要的。等待我们是否走在正确的轨道上。

从评论中更新

要获得您期望的结果,我已将Get-Date更改为变量$today。出于测试目的,我将$today设置为2月27日。只需将$today = Get-Date更改为生产数据。我还更新了第四个条件的逻辑。如果现在有效,请告诉我。

最后还添加了另一个Select子句,将输出配置为输入。您将看到第三列是否为空" N / A"被放回文件中。

此外,我没有收到您的测试代码的错误。如果您有更多问题,请告诉我。

信用到期的信用

如何确定月份差异的计算来自这个答案:https://stackoverflow.com/a/21053007/3829407