Come eliminare l'ultima colonna di un file in Linux


25

Voglio eliminare l'ultima colonna di un file txt, mentre non so quale sia il numero di colonna. Come potrei farlo?

Esempio:

Ingresso:

1223 1234 1323 ... 2222 123
1233 1234 1233 ... 3444 125
0000 5553 3455 ... 2334 222

E voglio che il mio output sia:

1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334

Ci sono molti modi per farlo ... per favore aggiungi un esempio e il tuo output previsto da esso ..
heemayl

@heemayl ok ho fatto
zara

Grazie..la scheda colonne è separata o spazio separato?
heemayl

@heemayl space is deliminator
zara

Risposte:


43

Con awk:

awk 'NF{NF-=1};1' <in >out

o:

awk 'NF{NF--};1' <in >out

o:

awk 'NF{--NF};1' <in >out

Anche se questo sembra voodoo, funziona. Ci sono tre parti per ciascuno di questi comandi awk.

Il primo è NF, che è un prerequisito per la seconda parte. NFè una variabile che contiene il numero di campi in una riga. In AWK, le cose sono vere se non sono 0 o stringa vuota "". Quindi, la seconda parte (dove NFè decrementata) accade solo se NFnon è 0.

La seconda parte (o NF-=1 NF--o --NF) sta semplicemente sottraendo una dalla NFvariabile. Ciò impedisce la stampa dell'ultimo campo, perché quando si modifica un campo (rimuovendo l'ultimo campo in questo caso), awkricostruire $0, concatenare tutti i campi separati dallo spazio per impostazione predefinita. $0non conteneva più l'ultimo campo.

La parte finale è 1. Non è magico, è solo usato come espressione che significa true. Se awkun'espressione viene valutata vera senza alcuna azione associata, l' awkazione predefinita è print $0.


@JJoao: Ah, grazie, mi sono dimenticato --. Una nota, al momento, è necessario ;1per POSIX conforme.
cuonglm,

Il mio istinto iniziale sarebbe quello di usare un ciclo for, ma questo è molto più conciso e intelligente.
Sergiy Kolodyazhnyy,

5
Vale la pena notare che se si utilizza un delimitatore non predefinito, è necessario apportare alcune modifiche. Supponendo che ,sia il tuo delimitatore:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
Mr. Llama,

1
L'effetto del decremento di NF è un comportamento indefinito da POSIX: otterrai un output diverso a seconda del awk che stai eseguendo. Alcuni awk rimuoveranno l'ultimo campo come desideri, altri non faranno nulla e altri potrebbero segnalare un errore di sintassi o altro.
Ed Morton,

16

Utilizzando grepcon PCRE:

$ grep -Po '.*(?=\s+[^\s]+$)' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334

Usando GNU sed:

$ sed -r 's/(.*)\s+[^\s]+$/\1/' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334

1
@ramin Certo..puoi chiederlo per favore come una nuova domanda (ecco come funziona questo sito) :)
heemayl

@ramin Ti dà qualche limite di tempo o qualche avvertimento?
heemayl

dice che questo è fuori discussione normale!
Zara,

@ramin Ok..fammi contattare un amministratore, potrebbero essere in grado di aiutarti..btw hai controllato qualche vecchio QA riguardo alla tua domanda? è una possibilità che la domanda sia già stata posta e risposta ..
heemayl

3
Non porre domande super basilari come " come posso rinominare un nome di file in Linux ". Usa Google.
Christoffer Hammarström,

11

Utilizzando Perl:

perl -lane '$,=" ";pop(@F);print(@F)' in

Utilizzando rev+ cut:

rev in | cut -d ' ' -f 2- | rev

5

Usando GNU sed:

sed -r 's/\s+\S+$//' input.txt

Più in generale, questo funziona con la BSD sed in OSX, così come GNU sed:

sed 's/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//' input.txt

1

Se il delimitatore è sempre un singolo carattere (quindi due o più delimitatori consecutivi designano campi vuoti), potresti headsolo la prima riga del tuo file di input, contare i delimitatori ( ndelimitatori significa che il numero di campi è n+1) quindi utilizzare cutper stampare dal 1campo st fino al ncampo th (dal secondo all'ultimo), ad es. con input delimitato da tabulazioni:

n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l)
cut -f1-$n infile > outfile

o ad esempio con un file CSV :

n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l)
cut -d, -f1-$n infile > outfile

Eseguirò alcuni benchmark più tardi se avrò il tempo, ma con un enorme input penso che questa soluzione dovrebbe essere più veloce di altre soluzioni che usano regex in quanto questa esegue un'elaborazione minima sulla prima riga per ottenere il no. di campi e quindi utilizza ciò cutche è ottimizzato per questo lavoro.


1

Portabilmente puoi usare uno di questi:

sed 's/[[:space:]]*[^[:space:]]*$//' file

awk '{sub(/[[:space:]]*[^[:space:]]*$/,"")}1' file

0

Usando vim:

Apri il file in vim

vim <filename> 

Vai alla prima riga, nel caso in cui il cursore sia posizionato altrove.

gg

Crea una macro denominata "q" qq, che va sul retro della riga corrente $, quindi torna all'ultimo spazio F(maiuscola F, seguito da SPAZIO letterale) quindi elimina dalla posizione corrente fino alla fine della riga, Dscendi alla riga successiva je interrompere la registrazione macro con q.

qq$F Djq

Ora possiamo ripetere la nostra macro con @qper ogni riga.
Possiamo anche premere @@per ripetere l'ultima macro o anche più semplice:

99@q

per ripetere la macro 99 volte.
Nota: il numero non deve corrispondere esattamente alle linee.


0

Per le persone che hanno un problema simile ma con diversi separatori di campo questo awkmetodo conserverà correttamente il separatore di campo:

$ cat file 
foo.bar.baz
baz.bar.foo
$ awk -F'.' 'sub(FS $NF,x)' file
foo.bar
baz.bar
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.