简体   繁体   English

如何使用 PowerShell 打印文件的某一行?

[英]How to print a certain line of a file with PowerShell?

I don't have a decent text editor on this server, but I need to see what's causing an error on line 10 of a certain file.我在这台服务器上没有像样的文本编辑器,但我需要看看是什么导致了某个文件的第 10 行出现错误。 I do have PowerShell though...不过我确实有 PowerShell...

It's as easy as using select:就像使用 select 一样简单:

Get-Content file.txt | Select -Index (line - 1)

Eg to get line 5例如获得第 5 行

Get-Content file.txt | Select -Index 4

Or you can use:或者你可以使用:

(Get-Content file.txt)[4]

This will show the 10th line of myfile.txt:这将显示 myfile.txt 的第 10 行:

get-content myfile.txt | select -first 1 -skip 9

both -first and -skip are optional parameters, and -context , or -last may be useful in similar situations. -first-skip都是可选参数, -context-last在类似情况下可能很有用。

You can use the -TotalCount parameter of the Get-Content cmdlet to read the first n lines, then use Select-Object to return only the n th line:您可以使用Get-Content cmdlet-TotalCount参数读取前n行,然后使用Select-Object仅返回第n行:

Get-Content file.txt -TotalCount 9 | Select-Object -Last 1;

Per the comment from @CB this should improve performance by only reading up to and including the n th line, rather than the entire file.根据@CB 的评论,这应该通过仅读取并包括第n行而不是整个文件来提高性能。 Note that you can use the aliases -First or -Head in place of -TotalCount .请注意,您可以使用别名-First-Head代替-TotalCount

Here's a function that uses .NET's System.IO classes directly:这是一个直接使用 .NET 的System.IO类的函数:

function GetLineAt([String] $path, [Int32] $index)
{
    [System.IO.FileMode] $mode = [System.IO.FileMode]::Open;
    [System.IO.FileAccess] $access = [System.IO.FileAccess]::Read;
    [System.IO.FileShare] $share = [System.IO.FileShare]::Read;
    [Int32] $bufferSize = 16 * 1024;
    [System.IO.FileOptions] $options = [System.IO.FileOptions]::SequentialScan;
    [System.Text.Encoding] $defaultEncoding = [System.Text.Encoding]::UTF8;
    # FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions) constructor
    # http://msdn.microsoft.com/library/d0y914c5.aspx
    [System.IO.FileStream] $input = New-Object `
        -TypeName 'System.IO.FileStream' `
        -ArgumentList ($path, $mode, $access, $share, $bufferSize, $options);
    # StreamReader(Stream, Encoding, Boolean, Int32) constructor
    # http://msdn.microsoft.com/library/ms143458.aspx
    [System.IO.StreamReader] $reader = New-Object `
        -TypeName 'System.IO.StreamReader' `
        -ArgumentList ($input, $defaultEncoding, $true, $bufferSize);
    [String] $line = $null;
    [Int32] $currentIndex = 0;

    try
    {
        while (($line = $reader.ReadLine()) -ne $null)
        {
            if ($currentIndex++ -eq $index)
            {
                return $line;
            }
        }
    }
    finally
    {
        # Close $reader and $input
        $reader.Close();
    }

    # There are less than ($index + 1) lines in the file
    return $null;
}

GetLineAt 'file.txt' 9;

Tweaking the $bufferSize variable might affect performance.调整$bufferSize变量可能会影响性能。 A more concise version that uses default buffer sizes and doesn't provide optimization hints could look like this:使用默认缓冲区大小且不提供优化提示的更简洁版本可能如下所示:

function GetLineAt([String] $path, [Int32] $index)
{
    # StreamReader(String, Boolean) constructor
    # http://msdn.microsoft.com/library/9y86s1a9.aspx
    [System.IO.StreamReader] $reader = New-Object `
        -TypeName 'System.IO.StreamReader' `
        -ArgumentList ($path, $true);
    [String] $line = $null;
    [Int32] $currentIndex = 0;

    try
    {
        while (($line = $reader.ReadLine()) -ne $null)
        {
            if ($currentIndex++ -eq $index)
            {
                return $line;
            }
        }
    }
    finally
    {
        $reader.Close();
    }

    # There are less than ($index + 1) lines in the file
    return $null;
}

GetLineAt 'file.txt' 9;

Just for fun, here some test:只是为了好玩,这里有一些测试:

# Added this for @Graimer's request ;) (not same computer, but one with HD little more
# performant...)
> measure-command { Get-Content ita\ita.txt -TotalCount 260000 | Select-Object -Last 1 }


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 28
Milliseconds      : 893
Ticks             : 288932649
TotalDays         : 0,000334412788194444
TotalHours        : 0,00802590691666667
TotalMinutes      : 0,481554415
TotalSeconds      : 28,8932649
TotalMilliseconds : 28893,2649


> measure-command { (gc "c:\ps\ita\ita.txt")[260000] }


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 9
Milliseconds      : 257
Ticks             : 92572893
TotalDays         : 0,000107144552083333
TotalHours        : 0,00257146925
TotalMinutes      : 0,154288155
TotalSeconds      : 9,2572893
TotalMilliseconds : 9257,2893


> measure-command { ([System.IO.File]::ReadAllLines("c:\ps\ita\ita.txt"))[260000] }


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 234
Ticks             : 2348059
TotalDays         : 2,71766087962963E-06
TotalHours        : 6,52238611111111E-05
TotalMinutes      : 0,00391343166666667
TotalSeconds      : 0,2348059
TotalMilliseconds : 234,8059



> measure-command {get-content .\ita\ita.txt | select -index 260000}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 36
Milliseconds      : 591
Ticks             : 365912596
TotalDays         : 0,000423509949074074
TotalHours        : 0,0101642387777778
TotalMinutes      : 0,609854326666667
TotalSeconds      : 36,5912596
TotalMilliseconds : 36591,2596

the winner is : ([System.IO.File]::ReadAllLines( path ))[index]获胜者是: ([System.IO.File]::ReadAllLines( path ))[index]

To reduce memory consumption and to speed up the search you may use -ReadCount option of Get-Content cmdlet ( https://technet.microsoft.com/ru-ru/library/hh849787.aspx ).为了减少内存消耗并加快搜索速度,您可以使用 Get-Content cmdlet ( https://technet.microsoft.com/ru-ru/library/hh849787.aspx ) 的 -ReadCount 选项。

This may save hours when you working with large files.当您处理大文件时,这可能会节省数小时。

Here is an example:下面是一个例子:

$n = 60699010
$src = 'hugefile.csv'
$batch = 100
$timer = [Diagnostics.Stopwatch]::StartNew()

$count = 0
Get-Content $src -ReadCount $batch -TotalCount $n | %  { 
    $count += $_.Length
    if ($count -ge $n ) {
        $_[($n - $count + $_.Length - 1)]
    }
}

$timer.Stop()
$timer.Elapsed

This prints $n'th line and elapsed time.这将打印第 $n 行和经过的时间。

I know that this is an old question, but despite being one of the most viewed ones for this topic, none of the answers are completely satisfactory to me.我知道这是一个老问题,但尽管是该主题中查看次数最多的问题之一,但没有一个答案让我完全满意。 Get-Content is easy to use, but it shows its limitations when it comes to really large text files (eg >5 GB). Get-Content易于使用,但当涉及到非常大的文本文件(例如 >5 GB)时,它显示出其局限性。

I figured out a solution which does not require the entire file to be loaded into main memory and that is way faster than Get-Content (almost as fast as sed on Linux, like this ):我想出了一个解决方案,它不需要将整个文件加载到主内存中,并且比Get-Content快得多(几乎和 Linux 上的sed一样快,如下所示):

[Linq.Enumerable]::ElementAt([System.IO.File]::ReadLines("<path_to_file>"), <index>)

This takes about 4 seconds on my machine to find a line in the middle of a ~4.5 GB file, whereas (Get-Content -Path <path_to_file> -TotalCount <index>)[-1] takes about 35 seconds.这在我的机器上大约需要 4 秒才能在 ~4.5 GB 文件的中间找到一行,而(Get-Content -Path <path_to_file> -TotalCount <index>)[-1]需要大约 35 秒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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