Come troncare il file per linee?


13

Ho un gran numero di file, alcuni dei quali sono molto lunghi. Vorrei troncarli a una certa dimensione se sono più grandi rimuovendo la fine del file. Ma voglio solo rimuovere intere righe. Come posso fare questo? Sembra il tipo di cose che verrebbero gestite dalla toolchain di Linux, ma non conosco il comando giusto.

Ad esempio, supponiamo di avere un file da 120.000 byte con linee da 300 byte e sto cercando di troncarlo a 10.000 byte. Le prime 33 righe dovrebbero rimanere (9900 byte) e il resto dovrebbe essere tagliato. Non voglio tagliare esattamente a 10.000 byte, poiché ciò lascerebbe una linea parziale.

Naturalmente i file hanno lunghezze diverse e le linee non sono tutte della stessa lunghezza.

Idealmente i file risultanti verrebbero resi leggermente più corti piuttosto che leggermente più lunghi (se il punto di interruzione si trova su una linea lunga) ma questo non è troppo importante, potrebbe essere un po 'più lungo se fosse più semplice. Vorrei che le modifiche venissero apportate direttamente ai file (bene, possibilmente il nuovo file copiato altrove, l'originale eliminato e il nuovo file spostato, ma è lo stesso dal punto di vista dell'utente). Una soluzione che reindirizza i dati in un sacco di posti e poi indietro invita la possibilità di corrompere il file e vorrei evitare che ...


Ho cancellato la mia risposta ... Immagino che la dimensione del file in Byte non fosse troppo chiara, scusa. Forse potresti modificare la tua domanda e chiarire quella parte (ad esempio con un esempio)?
slhck,

@slhck: Mi dispiace vederti perdere la reputazione solo perché non ero chiaro ... fammi vedere se riesco a risolvere il problema.
Charles

Non preoccuparti, avrei dovuto solo chiedere, scusa :)
slhck,

Risposte:


1

La sed/ wccomplessità può essere evitata nelle risposte precedenti se awkviene utilizzata. Utilizzando l'esempio fornito dall'OP (che mostra le linee complete prima di 10000 byte):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Mostra anche la riga completa contenente 10000 ° byte se quel byte non si trova alla fine della riga:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

La risposta sopra presuppone:

  1. I file di testo sono di Unix line terminator ( \n). Per i file di testo Dos / Windows ( \r\n), passare length() + 1alength() + 2
  2. Il file di testo contiene solo caratteri a byte singolo. Se è presente un carattere multibyte (ad esempio in ambiente Unicode), imposta l'ambiente LC_CTYPE=Cper forzare l'interpretazione a livello di byte.

14

L' sedapproccio va bene, ma non lo è fare il loop su tutte le linee. Se sai quante righe vuoi mantenere (per avere un esempio, io uso 99 qui), puoi farlo in questo modo:

sed -i '100,$ d' myfile.txt

Spiegazione: sedè un processore di espressioni regolari. Con l'opzione -ifornita, elabora direttamente un file ("inline"), invece di leggerlo e scrivere i risultati nell'output standard. 100,$significa semplicemente "dalla riga 100 alla fine del file" - ed è seguito dal comando d, che probabilmente hai indovinato correttamente per "eliminare". Quindi, in breve, il comando significa: "Elimina tutte le righe dalla riga 100 alla fine del file da myfile.txt". 100 è la prima riga da eliminare, poiché si desidera mantenere 99 righe.

Modifica: se, d'altra parte, ci sono file di registro in cui si desidera conservare, ad esempio le ultime 100 righe:

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

Cosa sta succedendo qui:

  • [ $(wc -l myfile.txt) -gt 100 ]: eseguire le seguenti operazioni solo se il file ha più di 100 righe
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): calcola il numero di righe da eliminare (ovvero tutte le righe del file tranne le (ultime) 100 da conservare)
  • 1, $((..)) d: rimuove tutte le righe dalla prima alla riga calcolata

EDIT: poiché la domanda è stata appena modificata per fornire ulteriori dettagli, includerò anche queste informazioni aggiuntive con la mia risposta. I fatti aggiunti sono:

  • una dimensione specifica deve rimanere con il file (10.000 byte)
  • ogni riga ha una dimensione specifica in byte (300 byte nell'esempio)

Da questi dati è possibile calcolare il numero di righe da rimanere come "/", che con l'esempio significherebbe 33 righe. Il termine shell per il calcolo: $((size_to_remain / linesize))(almeno su Linux usando Bash, il risultato è un numero intero). Il comando modificato ora dovrebbe leggere:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Poiché le dimensioni sono note in anticipo, non è più necessario un calcolo incorporato nel sedcomando. Ma per flessibilità, all'interno di alcuni script di shell si possono usare le variabili.

Per l'elaborazione condizionale in base alla dimensione del file, si può usare il seguente costrutto "test":

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

il che significa: "se la dimensione $filesupera 100kB, do ..." ( ls -lkelenca la dimensione del file in kB nella posizione 5, quindi awkviene utilizzata per estrarre esattamente questo).


L'OP vuole tagliare il file in base a una determinata dimensione di byte, non solo alla lunghezza in termini di linee. Ho cancellato la mia risposta coinvolgendo head -n.
slhck,

@slhck Grazie per la notifica. Sì, l'OP ha appena modificato la sua domanda per rendere più chiara l'intenzione. Dato che ha i mezzi per calcolare quanti byte ha ogni riga, la mia risposta rimane valida in linea di principio, poiché può calcolare il numero di righe rimanenti e quindi utilizzare il mio approccio per gestire i file. Forse faccio una breve osservazione su questo nella mia risposta.
Izzy,

No: le dimensioni non sono note in anticipo. Questo è stato un esempio. Ogni file avrà una dimensione diversa e le linee sono di lunghezza irregolare. Alcuni file non devono essere troncati affatto.
Charles,

Oh, ancora una volta ... Beh, alcune cose sono difficili da spiegare chiaramente (troppe facette). Per quanto riguarda i file che non necessitano di troncamento, è probabilmente basato sulla dimensione del file? Questo può essere coperto. Ma se non si conosce nemmeno una dimensione media della linea, questa parte diventa difficile: al momento non riesco a pensare a una soluzione semplice (senza troppe spese generali).
Izzy,

Al momento, tutto ciò che posso venire in mente riguarderebbe, ad esempio, l'ottenimento delle prime n righe, il calcolo di una lunghezza media basata su di esse e l'utilizzo di questo valore. Ti sarebbe d'aiuto?
Izzy,

0

Non riuscendo a trovare un comando per farlo, ho scritto uno script rapido (non testato):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done

-1

Puoi usare il comando linux sed per rimuovere le linee da un file. Il seguente comando elimina l'ultima riga di nomefile.txt:

sed '$d' filename.txt

Con awk or find puoi cercare un modello che corrisponda al tuo comando sed. Prima cerchi con awk o trova i file che vuoi abbreviare e poi puoi rimuovere le linee con sed.


-1

Ho fatto qualcosa di simile con la coda. Per mantenere solo le ultime 10.000 righe in questo caso:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.