Was entspricht Bashs Katze -n in PowerShell?


7

Ich möchte cateine Datei erstellen und die Zeilennummer jeder ausgegebenen Zeile ausgeben.

In PowerShell wird jedoch catein Array ausgegeben. Daher stellt sich effektiv die Frage: Wie drucke ich den Index jedes Elements, während es an die Konsole ausgegeben wird ...?

Ich habe so etwas versucht:

$k = cat foo.js
$k | foreach { $index = $k.IndexOf($_) + 1; write "$index : $_"; } | more

Es gab mir einige seltsame Ergebnisse. Einige Zeilennummern werden wiederholt. Was ist ein eleganter und zuverlässigerer Weg, dies zu tun?


4
cat -nist eigentlich kein Teil von bash - es ist Teil von cat, was ein separates Programm ist - und es gibt keinen Grund zu der Annahme, dass jeder Computer mit installiertem bash es unterstützt.
Charles Duffy

Antworten:


5

Ich möchte eine Datei katzen und die Zeilennummer jeder ausgegebenen Zeile ausgeben.

Verwenden Sie den folgenden Befehl:

$counter = 0; get-content .\test.txt | % { $counter++; write-host "`t$counter` $_" }

Wie in den Kommentaren ausgeführt:

  • Es kann besser sein, write-outputstattdessen zu verwenden, write-hostda dies eine weitere Verarbeitung der Ausgabe ermöglicht.
  • echo ist ein Alias ​​für write-output

Der obige Befehl lautet also:

$counter = 0; get-content .\test.txt | % { $counter++; echo "`t$counter` $_" }

Beispielausgabe:

> type test.txt
foo
//approved
bar
// approved
foo
/*
approved
*/
bar

> $counter = 0; get-content .\test.txt | % { $counter++; echo "`t$counter` $_" }
        1 foo
        2 //approved
        3 bar
        4 // approved
        5 foo
        6 /*
        7 approved
        8 */
        9 bar
>

Beispielausgabe von Cygwin cat -nzum Vergleich:

$ cat -n test.txt
     1  foo
     2  //approved
     3  bar
     4  // approved
     5  foo
     6  /*
     7  approved
     8  */
     9  bar
$

Hmmm ... Ja, ich denke das ist besser als meins. Obwohl ich könnte das ändern Write-Host... zu "{0,5} {1}" -f $counter,$_(und ich kann immer noch nicht die Backticks Arbeit richtig in backticked Text bekommen ...)
Jeff Zeitlin

Write-Output ist hier eine bessere Wahl als Write-Host, wenn Sie die Daten zum Speichern oder zur weiteren Verarbeitung an eine Pipeline senden möchten.
Zoredache

@ Zoredache Sehr wahrscheinlich. Ich bin kein PowerShell-Experte :)
DavidPostill

@DavidPostill Standardmäßig ist es einfacher, nur echoAliase zu verwenden Write-Output. Write-Hostist besonders. Es ist wie direkt zu schreiben /dev/tty.
Bob

@ Bob Danke für die Erklärung. Ich habe die Antwort aktualisiert.
DavidPostill

11

Sie missbrauchen möglicherweise Select-String dafür:

Select-String -Pattern .* -Path .\foo.txt | select LineNumber, Line

Beispielausgabe:

LineNumber Line
---------- ----
         1 a   
         2     
         3 b   
         4     
         5 c   

cooler deklarativer Code im Gegensatz zu den anderen Antworten
Katze

1

IndexOf()stimmt mit dem ersten Auftreten des Werts überein , sodass Ihre doppelten Zeilennummern unter Verwendung Ihres Originalcodes bedeuten, dass Sie mehrere Zeilen in der Datei haben, die identisch sind. Versuche Folgendes:

$k = Get-Content -Path foo.js
$l = 0 
while ($l -lt $k.length) {
    "{0,5}:  {1}" -f $l,$k[$l]
    $l++
}

1

Alles, woran Sie an der Eingabeaufforderung lange denken müssen, ist nicht elegant. Eleganz wäre also, genau das zu bekommen, was Sie brauchen, es in ein Skript zu schreiben und das Skript bei Bedarf aufzurufen. Um genau dieses Problem eleganter und kraftvoller zu lösen, als ich es selbst könnte, habe ich hier das Jeffrey Hicks-Skript verwendet: http://jdhitsolutions.com/blog/scripting/445/more-fun-with-get-numberedcontent/

Beispiel: Get-NumberedContent. \ README.txt Ausgabebeispiel:

369 | The Java(TM) Runtime Environment (JRE) and the JavaFX(TM) runtime are 
370 | products of Sun Microsystems(TM), Inc. 
371 | 
372 | Copyright © 2008 Sun Microsystems, Inc. 
373 | 4150 Network Circle, Santa Clara, California 95054, U.S.A. 
374 | All rights reserved.

Skript unten für den Fall, dass der Link jemals fehlschlägt:

#Requires -version 2.0

# Jeffery Hicks
# http://jdhitsolutions.com/blog
# follow on Twitter: http://twitter.com/JeffHicks
# "Those who forget to script are doomed to repeat their work."

#  ****************************************************************
#  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
#  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
#  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
#  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
#  ****************************************************************


Function Get-NumberedContent {
#Requires -version 2.0

<#
.Synopsis
    Display file contents in a numbered fashion.
.Description
    This function will display the contents of a text file as numbered output. If the file is 
    a script file, commented lines will be displayed in Green. Unlike Get-Content, the output
    is written to the console using Write-Host. This function is primarily meant as a console 
    based file viewer.It does not write to the pipeline unless you use the -PassThru parameter
    in which case you will get no colorized output. 

    For script files, or any file for that matter, you can specify a character ad the comment
    character. The default comment character is the #. Any line that begins with that chara-
    cter will be treated as a comment. You can skip comments by using -NoComment. Otherwise 
    the line will print in a green font. You can override the fontcolor using -CommentColor. 

    Use -NoBlank to suppress output of any blank lines. You can also combine -NoBlank and 
    -NoComment to get a very short numbered line output.

    Line 0 will display the full filename and path

.Parameter Filepath
    The filename and path.
.Parameter CommentCharacter
    The character to use as the comment character. The default is #. The parameter has an 
    alias of "Char".
.Parameter CommentColor
    The font color to use for commented lines. The default is green. This parameter has an
    alias of "Color"
.Parameter NoComments
    If the file is a script file, -NoComments will suppress any lines that begin with the 
    appropriate comment character.
.Parameter NoBlanks
    Suppress output of any blank lines. Line tabs and spacing will be maintained but blank
    lines will not be displayed.
.Parameter Passthru
    Write the output to the pipeline. 
.Example
    PS C:\> Get-NumberedContent c:\scripts\test.ps1

    Display line numbered content of Test.ps1 using the default comment character (#) and the
    default comment color, Green.
.Example
    PS C:\> Get-NumberedContent c:\scripts\update.vbs -nocomment -char "'"

    Display the results of update.vbs without and lines that start with the comment character
    for VBS scripts. This expression is using the parameter alias CHAR for -CommentCharacter.
.Example
    PS C:\> get-numberedcontent c:\files\report.ext -noblanks -pass | out-file NumReport.txt

    Display the contents of c:\files\report.txt without any blank lines and pass to the pipeline.
    The pipelined output is then sent to the Out-File cmdlet.
.Example
    PS C:\> dir c:\TEST\*.CSV | get-numberedcontent -commentCharacter ";" -commentColor "Red"  -noblanks

    Get the content for every CSV file in the Test directory. Commented lines that start with ;
    will be displayed in a red color and blank lines will be suppressed.

.Inputs
    Accepts strings as pipelined input
.Outputs
    None

.Link
   Get-Content


.Notes
 NAME:      Get-NumberedContent
 VERSION:   2.0
 AUTHOR:    Jeffery Hicks
            http://jdhitsolutions.com/blog
 LASTEDIT:  10/13/2009 


#>


[CmdletBinding()]

    param (
        [Parameter(
         ValueFromPipeline=$True,
         Position=0,
         Mandatory=$True,
         HelpMessage="The filename and path of a text file.")] 
         [string]$Filename,

         [Parameter(
         ValueFromPipeline=$False,
         Mandatory=$False,
         HelpMessage="The comment character for a specific file type.")] 
         [Alias("Char")]
         [string]$CommentCharacter="#",

         [Parameter(
         ValueFromPipeline=$False,
         Mandatory=$False,
         HelpMessage="The comment character color. Default is Green.")] 
         [ValidateSet("Black","DarkBlue","Blue","DarkGreen","Green","DarkCyan","Cyan",
         "DarkRed","Red","Magenta","White","DarkGray","Gray","DarkYellow","Yellow")] 
         [Alias("Color")]
         [string]$CommentColor="Green",

         [Parameter(
         ValueFromPipeline=$False,
         Mandatory=$False,
         HelpMessage="Suppress comment lines for script files.")] 
         [switch]$NoComment,

         [Parameter(
         ValueFromPipeline=$False,
         Mandatory=$False,
         HelpMessage="Suppress blank lines.")] 
         [switch]$NoBlank,

         [Parameter(
         ValueFromPipeline=$False,
         Mandatory=$False,
         HelpMessage="Write object to the pipeline instead of the console.")] 
         [switch]$Passthru

         )

Begin {
    if ($NoComment) { Write-Debug "No comments"}
    if ($NoBlank) {Write-Debug "No blank lines"}
    Write-Debug "Comment character is #CommentCharacter"
    Write-Debug "Comment color is $CommentColor"
    if ($passthru) {Write-Debug "Passthru"}

} #end Begin

Process {

    if ($_) {
        $Filename=$_
    $FullName=$_.Fullname
    }
    else {
    $Fullname=$Filename
    }

    write-debug "Testing $filename"
    If (Test-Path $filename) {
        $counter = -1

        write-debug "Getting content"
        $content=get-content $Filename

        #get the total number of lines and then the length
        #of that number so the number of leading zeros can be
        #calculated more accurately
        write-debug "Calculating number of lines"
        $c=($content.count).ToSTring().Length

        write-debug "Padding line numbers to $c places"
        write-debug "Processing content"
        $content | foreach { 
            #default font color
            $fcolor="White"

            #determine if line is a blank
            if ($_.Trim().Length -gt 0) {
                $Empty=$False
                write-debug "Line is not empty"
            }
            else {
                write-debug "Line is empty"
                $Empty=$True
             }


             #determine if line is a comment

            $isComment=$False

             if ($_.Trim().StartsWith($CommentCharacter))  {
                   write-debug "Comment line found"
                   $fcolor=$CommentColor
                   $isComment=$True
                }


            if (($NoBlank -AND $Empty) -OR ($NoComment -AND $IsComment )) {
                write-debug "Skipping line"
              }

            else {

         $counter++

                if ($counter -eq 0) {
                    $line = "{0:d$($c)} | {1}" -f $counter,$FullName.ToUpper() 
            $fcolor="White"               
                }

                else {

                    #write a line number with leading zeros the | bar and then the line of text from the file
                    #trimming off any trailing spaces
                    $line = "{0:d$($c)} | {1}" -f $counter,$_.TrimEnd()
                }

                if ($Passthru) {
                    write $line
                }
                else {
                   Write-Host $line -foregroundcolor $fcolor
                }
            } #else not a blank line


         } #end ForEach
    } #end if Test-Path
    else {
        Write-Warning "Failed to find $filename"
        }
  } #end Process

 End {
  Write-Debug "Ending and exiting"

 }

 } #end function

Set-Alias gnc Get-NumberedContent

1

Ähnlich wie der Code von DavidPostill, jedoch mit der rechtsbündigen Nummer wie cat -n

$cnt=0;gc .\test.txt|%{$cnt++;"{0,6} {1}" -f $cnt,$_}

Oder mit dem gleichen Ergebnis:

select-string -path .\test.txt "^" |%{"{0,6} {1}" -f $_.LinenUmber,$_.Line}

Beispielausgabe:

PS> $cnt=0;gc .\test.txt |%{$cnt++;"{0,6} {1}" -f $cnt,$_}
     1 foo
     2 //approved
     3 bar
     4 // approved
     5 foo
     6 /*
     7 approved
     8 */
     9 bar

1

catin PowerShell ist eigentlich ein Alias ​​für Get-Content. Sie können dies von sehen Get-Alias cat. Viele der einfachen nix-Befehle gaben PS-Entsprechungen an, um Benutzern den Einstieg in PowerShell zu erleichtern. Sie sind keine perfekten Spiegel, aber sie versuchen es.

Es ist auch nicht erforderlich, ausgefallene Fußarbeit mit Get-Contentder Ausgabe zu machen, um die Zeilennummern zu berechnen. Das erledigt das Cmdlet bereits für Sie.

Get-Content C:\temp\pingtst.csv | ForEach-Object{"$($_.readcount):  $_"} 

Zugegeben, die Ausgabe ist nicht perfekt und linksbündig, aber Sie können dies beheben, indem Sie Ihre eigenen Funktionen und Cmdlets rollen. PowerShell arbeitet mit maximaler Leistung mit Objekten. Wenn Sie Ihre Datei also in etwas anderes verwandeln, sieht dies folgendermaßen aus:

PS C:\Windows\system32> Get-Content C:\temp\pingtst.csv | Select-Object ReadCount,@{Name="Line";Expression={"$_"}}

ReadCount Line      
--------- ----      
        1 localhost 
        2 localhost0
        3 localhost1
        4 localhost2

Denken Sie daran , es gibt viele weitere Möglichkeiten , um Hilfe wie -Head, -Tail, -TotalCountusw. , die Funktionalität dieser scheinbar einfachen Cmdlets hinzufügen können.

Obwohl ich sicher bin, dass das nicht genau das ist, was Sie sich erhofft haben. Der Punkt ist, Get-Contentdass die Zeilennummern bereits bekannt sind, sodass keine Zählungen oder ähnliches erforderlich sind.


1
Get-Content -Path D:\in\demo.txt | % { "{0,5} {1}" -f $PSItem.Readcount, $PSItem }

Oder vielleicht das Folgende, um eine Zeile (in der PowerShell-Ausgabe) pro Zeile (in der Datei) zu garantieren.

Get-Content -Path D:\in\demo.txt -ReadCount 1 | % { "{0,5} {1}" -f $PSItem.Readcount, $PSItem }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.