2013-01-18 15 views
5

Devo cercare solo la 1a riga e l'ultima riga in un file di testo per trovare un "-" e rimuoverlo. Come posso farlo? Ho provato la stringa di selezione, ma non so per trovare la prima e l'ultima riga e solo rimuovere "-" da lì.Come posso cercare la prima riga e l'ultima riga in un file di testo?

Ecco ciò che il file di testo si presenta come:

% 01-A247M15 G70 
N0001 G30 G17 X-100 Y-100 Z0 
N0002 G31 G90 X100 Y100 Z45 
N0003 ; --PART NO.: NC-HON.PHX01.COVER-SHOE.DET-1000.050 
N0004 ; --TOOL: 8.55 X .3937 
N0005 ; 
N0006 % 01-A247M15 G70 

qualcosa di simile?

$1 = Get-Content C:\work\test\01.I 

$1 | select-object -index 0, ($1.count-1) 

risposta

5

Ok, quindi dopo guardando questo per un po ', ho deciso ci doveva essere un modo per fare questo con un uno di linea. Eccolo:

(gc "c:\myfile.txt") | % -Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)} -Process {if ($_ -eq $test[0] -or $_ -eq $test[-1]) { $_ -replace "-" } else { $_ }} | Set-Content "c:\myfile.txt" 

Qui è una ripartizione di ciò che questo sta facendo:

In primo luogo, gli alias per chi ormai familiare. Ho solo li metto in perché il comando è abbastanza lungo così com'è, quindi questo aiuta a mantenere le cose gestibile:

  1. gc significa Get-Content
  2. % significa Foreach
  3. $_ è per il valore di pipeline corrente (questo isn 't un alias, ma ho pensato che definirei da quando hai detto che eri nuovo)

Ok, ora qui è ciò che sta accadendo in questo:

  1. (gc "c:\myfile.txt") | -> Ottiene il contenuto di c:\myfile.txt e lo manda giù la linea
  2. % -> Fa un ciclo foreach (passa attraverso ogni elemento in cantiere individualmente)
  3. -Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)} -> Questo è un blocco iniziale, esegue tutto qui prima che entri nella roba della pipeline.Sta caricando la prima e l'ultima riga di c:\myfile.txt in un array in modo che possiamo controllare il primo e l'ultimo articolo
  4. -Process {if ($_ -eq $test[0] -or $_ -eq $test[-1]) -> Questo esegue un controllo su ogni elemento nella pipeline, controllando se si tratta del primo o dell'ultimo elemento in il file
  5. { $_ -replace "-" } else { $_ } -> se è il primo o l'ultimo, lo fa la sostituzione, se non lo è, semplicemente lascia da solo
  6. | Set-Content "c:\myfile.txt" -> Questo mette i nuovi valori all'interno del file.

prega di consultare i seguenti siti per ulteriori informazioni su ciascuno di questi elementi:

Get-Content uses
Get-Content definition
Foreach
The Pipeline
Begin and Process parte del foreach (questo sono di solito per funzione personalizzata, ma funzionano anche nel ciclo foreach)
If ... else dichiarazioni
Set-Content

Quindi stavo pensando a cosa fare se si voleva fare questo a molti file o si voleva farlo spesso. Ho deciso di fare una funzione che fa ciò che stai chiedendo. Ecco la funzione:

function Replace-FirstLast { 
    [CmdletBinding()] 
    param(
     [Parameter(` 
      Position=0, ` 
      Mandatory=$true)] 
     [String]$File, 
     [Parameter(` 
      Position=1, ` 
      Mandatory=$true)] 
     [ValidateNotNull()] 
     [regex]$Regex, 
     [Parameter(` 
      position=2, ` 
      Mandatory=$false)] 
     [string]$ReplaceWith="" 
    ) 

Begin { 
    $lines = Get-Content $File 
} #end begin 

Process { 
    foreach ($line in $lines) { 
     if ($line -eq $lines[0] ) { 
      $lines[0] = $line -replace $Regex,$ReplaceWith 
     } #end if 
     if ($line -eq $lines[-1]) { 
      $lines[-1] = $line -replace $Regex,$ReplaceWith 
     } 
    } #end foreach 
}#End process 

end { 
    $lines | Set-Content $File 
}#end end 

} #end function 

Questo creerà un comando chiamato Replace-FirstLast. Si sarebbe chiamato in questo modo:

Replace-FirstLast -File "C:\myfiles.txt" -Regex "-" -ReplaceWith "NewText" 

Il -Replacewith è facoltativo, se è vuoto sarà sufficiente rimuovere (valore di default di ""). -Regex sta cercando un'espressione regolare che corrisponda al tuo comando. Per informazioni sul posizionamento questo nel tuo profilo di controllo this article

Si prega di notare: Se il file è molto grande (diversi GB), questa non è la soluzione migliore. Ciò farebbe sì che l'intero file vada in memoria, il che potrebbe potenzialmente causare altri problemi.

+0

buona risposta, ma attenzione che se i file diventano enormi sarebbe meglio usare 'get-content -First 1' e' ... -Last 1' nell'istruzione begin in modo da non mantenere un file xxGB in memoria durante l'elaborazione. –

+0

Vero, ma poi siamo tornati al problema di non tornare indietro l'intero file, solo la prima e l'ultima riga. – Nick

+0

no. Ho detto nella dichiarazione iniziale 'foreach -begin {..here ...}'. :) –

6

prova:

$txt = get-content c:\myfile.txt 
$txt[0] = $txt[0] -replace '-' 
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-' 
$txt | set-content c:\myfile.txt 
+2

typos: $ a -> $ txt (ultima riga). $ txt [1] -> $ txt [0] :-) –

+0

Hoo .. Grazie! Risolto ora! –

+1

no .. ancora l'ultima riga (wtf è $ a?) Hehe: P –

1

È possibile utilizzare il cmdlet select-object per aiutarvi con questo, dal momento get-content sputa fondamentalmente un file di testo come una vasta gamma.

Così, si può fare qualcosa di simile

get-content "path_to_my_awesome_file" | select -first 1 -last 1 

Per rimuovere il trattino dopo di che, è possibile utilizzare l'opzione -Replace per trovare il trattino e rimuoverlo. È meglio che usare il metodo System.String.Replace(...) perché può abbinare le espressioni regex e sostituire anche interi array di stringhe!

Che sarebbe simile:

# gc = Get-Content. The parens tell Powershell to do whatever's inside of it 
# then treat it like a variable. 
(gc "path_to_my_awesome_file" | select -first 1 -last 1) -Replace '-','' 
+0

Pensavo anche a questo, l'unico problema che ho trovato è che ha bisogno dell'intero array. Se si tenta di reindirizzare questo file nel file, avrà solo la prima e l'ultima riga, non l'intero file con una prima e l'ultima riga modificate. – Nick

+1

Inoltre, dovrebbe essere 'gc' come alias di' Get-Content'. 'gci' è l'alias di' Get-ChildItem' – Nick

+0

Grazie per avermi corretto; Uso 'cat' così spesso, a volte dimentico l'intero nome del cmdlet. :) –

0

Una risposta più pulita a quanto sopra:

$Line_number_were_on = 0 
$Awesome_file = Get-Content "path_to_ridiculously_excellent_file" | %{ 
    $Line = $_ 
    if ($Line_number_were_on -eq $Awesome_file.Length) 
     { $Line -Replace '-','' } 
    else 
     { $Line } ; 
    $Line_number_were_on++ 
} 

mi piace one-liners, ma trovo che la leggibilità tende a soffrire a volte quando ho messo terseness sulla funzione. Se quello che stai facendo sarà parte di uno script che altre persone leggeranno/manterranno, la leggibilità potrebbe essere qualcosa da considerare.

+0

Ci sono alcuni problemi con questo script. Innanzitutto, funziona solo sull'ultima riga, non prima e ultima. In secondo luogo, '$ line' non viene mai reinserito in nulla, quindi il lavoro è terminato e poi perso, mai rimesso nel file. – Nick

0

seguito la risposta di Nick: ho bisogno di fare questo su tutti i file di testo nella directory e questo è quello che sto usando ora:

Get-ChildItem -Path "c:\work\test" -Filter *.i | where { !$_.PSIsContainer } | % { 
$txt = Get-Content $_.FullName; 
$txt[0] = $txt[0] -replace '-'; 
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-'; 
$txt | Set-Content $_.FullName 
} 

e sembra che funzioni bene ora.

0

Se il file è molto grande, è possibile che non si desideri leggere l'intero file per ottenere l'ultima riga. gc -Tail otterrà l'ultima linea molto rapidamente per te.

function GetFirstAndLastLine($path){ 

    return New-Object PSObject -Property @{   
     First = Get-Content $path -TotalCount 1 
     Last = Get-Content $path -Tail 1 
     } 
} 

GetFirstAndLastLine "u_ex150417.log" 

Ho provato questo su un file di registro da 20 GB e ha restituito immediatamente. La lettura del file richiede ore.

Sarà comunque necessario leggere il file se si desidera mantenere tutto il contenuto in uscita e si desidera solo rimuoverlo dalla fine. Usare il -Tail è un modo rapido per verificare se è lì.

Spero che aiuti.

0

processo semplice: Sostituire $ file.txt con il nome del file

Get-Content $ file_txt | Select-Object -last 1

Problemi correlati