Usando awk per stampare tutte le colonne dall’ennesimo all’ultimo

Questa linea ha funzionato fino a quando non ho avuto spazi bianchi nel secondo campo.

svn status | grep '\!' | gawk '{print $2;}' > removedProjs 

c’è un modo per far stampare awk tutto in $ 2 o più? ($ 3, $ 4 .. fino a quando non abbiamo più colonne?)

Suppongo che dovrei aggiungere che sto facendo questo in un ambiente Windows con Cygwin.

stamperà tutto ma molto prima colonna:

 awk '{$1=""; print $0}' somefile 

stamperà tutte ma due prime colonne:

 awk '{$1=$2=""; print $0}' somefile 

C’è una domanda doppia con una risposta più semplice usando il taglio:

  svn status | grep '\!' | cut -d\ -f2- 

-d specifica il delimitatore (spazio) , -f specifica l’elenco di colonne (tutte a partire dal 2 °)

È ansible utilizzare un ciclo for per eseguire il ciclo attraverso i campi di stampa da $ 2 a $ NF (variabile incorporata che rappresenta il numero di campi sulla linea).

Modifica: Poiché “stampa” aggiunge una nuova riga, ti consigliamo di memorizzare i risultati in un buffer:

 awk '{out=""; for(i=2;i<=NF;i++){out=out" "$i}; print out}' 

In alternativa, usa printf:

 awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}' 
 awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}' 

La mia risposta è basata su quella di VeeArr , ma ho notato che è iniziato con uno spazio bianco prima che stampasse la seconda colonna (e il resto). Dato che ho solo 1 punto reputazione, non posso commentare, quindi ecco come una nuova risposta:

iniziare con "out" come seconda colonna e quindi aggiungere tutte le altre colonne (se presenti). Questo va bene finché c'è una seconda colonna.

Ho provato personalmente tutte le risposte sopra menzionate, ma la maggior parte di esse erano un po ‘complesse o semplicemente non corrette. Il modo più semplice per farlo dal mio punto di vista è:

 awk -F" " '{ for (i=4; i<=NF; i++) print $i }' 
  1. Dove -F "" definisce il delimitatore per awk da usare. Nel mio caso è lo spazio bianco, che è anche il delimitatore predefinito per awk. Ciò significa che -F "" può essere ignorato.

  2. Dove NF definisce il numero totale di campi / colonne. Pertanto il ciclo inizierà dal 4 ° campo fino all'ultimo campo / colonna.

  3. Dove $ N recupera il valore del campo Nth. Quindi stampa $ i stamperà il campo / colonna corrente in base al conteggio del ciclo.

La maggior parte delle soluzioni con awk lascia uno spazio. Le opzioni qui evitano questo problema.

opzione 1

Una soluzione di taglio semplice (funziona solo con delimitatori singoli):

 command | cut -d' ' -f3- 

opzione 2

Forzare un ricalcolo di awk a volte rimuove lo spazio iniziale (OFS) aggiunto rimuovendo i primi campi (funziona con alcune versioni di awk):

 command | awk '{ $1=$2="";$0=$0;} NF=NF' 

Opzione 3

La stampa di ogni campo formattato con printf darà più controllo:

 $ in=' 1 2 3 4 5 6 7 8 ' $ echo "$in"|awk -vn=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}' 3 4 5 6 7 8 

Tuttavia, tutte le risposte precedenti cambiano tutte le FS ripetute tra i campi in OFS. Costruiamo un paio di opzioni che non lo fanno.

Opzione 4 (raccomandata)

Un ciclo con sub per rimuovere campi e delimitatori nella parte anteriore.
E usando il valore di FS invece di spazio (che potrebbe essere cambiato).
È più portabile e non innesca un cambiamento di FS in OFS: NOTA: ^[FS]* accetta un input con spazi iniziali.

 $ in=' 1 2 3 4 5 6 7 8 ' $ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+"; for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 ' 3 4 5 6 7 8 

Opzione 5

È abbastanza ansible creare una soluzione che non aggiunga spazi bianchi extra ( gensub o finali) e preservare gli spazi bianchi esistenti usando la funzione gensub di GNU awk, in questo modo:

 $ echo ' 1 2 3 4 5 6 7 8 ' | awk -vn=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; } { print(gensub(a""b""c,"",1)); }' 3 4 5 6 7 8 

Può anche essere usato per scambiare un gruppo di campi dato un conteggio n :

 $ echo ' 1 2 3 4 5 6 7 8 ' | awk -vn=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; } { d=gensub(a""b""c,"",1); e=gensub("^(.*)"d,"\\1",1,$0); print("|"d"|","!"e"!"); }' |3 4 5 6 7 8 | ! 1 2 ! 

Naturalmente, in tal caso, l'OFS viene utilizzato per separare entrambe le parti della linea e lo spazio bianco finale dei campi viene comunque stampato.

NOTA: [FS]* viene utilizzato per consentire gli spazi iniziali nella riga di input.

Questo mi irritava così tanto, mi sono seduto e ho scritto un parser di specifiche del campo simile a un cut , testato con GNU Awk 3.1.7.

Innanzitutto, crea un nuovo script di libreria Awk chiamato pfcut , ad es

 sudo nano /usr/share/awk/pfcut 

Quindi, incolla lo script qui sotto e salva. Dopodiché, ecco come appare l’utilizzo:

 $ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }' t1 t2 t3 t4 $ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }' t2 t3 t4 t5 t6 t7 $ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }' t1 t2 t4 t6 t7 

Per evitare di scrivere tutto ciò, immagino che il meglio che si possa fare (vedi altrimenti Caricare automaticamente una funzione utente all’avvio con awk? – Unix & Linux Stack Exchange ) è aggiungere un alias a ~/.bashrc ; ad esempio con:

 $ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc $ source ~/.bashrc # refresh bash aliases 

… quindi puoi semplicemente chiamare:

 $ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }' t1 t2 t4 t6 t7 

Ecco la fonte dello script pfcut :

 # pfcut - print fields like cut # # sdaau, GNU GPL # Nov, 2013 function spfcut(formatstring) { # parse format string numsplitscomma = split(formatstring, fsa, ","); numspecparts = 0; split("", parts); # clear/initialize array (for eg `tail` piping into `awk`) for(i=1;i<=numsplitscomma;i++) { commapart=fsa[i]; numsplitsminus = split(fsa[i], cpa, "-"); # assume here a range is always just two parts: "ab" # also assume user has already sorted the ranges #print numsplitsminus, cpa[1], cpa[2]; # debug if(numsplitsminus==2) { if ((cpa[1]) == "") cpa[1] = 1; if ((cpa[2]) == "") cpa[2] = NF; for(j=cpa[1];j<=cpa[2];j++) { parts[numspecparts++] = j; } } else parts[numspecparts++] = commapart; } n=asort(parts); outs=""; for(i=1;i<=n;i++) { outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS); #print(i, parts[i]); # debug } return outs; } function pfcut(formatstring) { print spfcut(formatstring); } 

Stampa di colonne a partire da # 2 (l’output non avrà uno spazio finale all’inizio):

 ls -l | awk '{sub(/[^ ]+ /, ""); print $0}' 

Funzionerebbe?

 awk '{print substr($0,length($1)+1);}' < file 

Tuttavia lascia alcuni spazi bianchi di fronte.

 echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}' 

questo usa awk per stampare tutto tranne l’ultimo campo

Questo è quello che ho preferito da tutti i consigli:

Stampa dalla sesta all’ultima colonna.

 ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}' 

o

 ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}' 
 awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }' 

lauhub ha proposto questa soluzione corretta, semplice e veloce qui

Se hai bisogno di colonne specifiche stampate con delimitatore arbitrario:

 awk '{print $3 " " $4}' 

col # 3 col # 4

 awk '{print $3 "anything" $4}' 

Col # 3anythingcol # 4

Quindi se hai uno spazio bianco in una colonna ci saranno due colonne, ma puoi collegarlo con qualsiasi delimitatore o senza di esso.

Soluzione Perl:

 perl -lane 'splice @F,0,1; print join " ",@F' file 

Queste opzioni della riga di comando sono utilizzate:

  • -n loop su ogni riga del file di input, non stampare automaticamente ogni riga

  • -l rimuove le newline prima dell’elaborazione e le aggiunge nuovamente in seguito

  • -a modalità autosplit – divide le linee di input nella matrice @F. Predefinito per la divisione su spazi vuoti

  • -e esegue il codice perl

splice @F,0,1 rimuove in modo pulito la colonna 0 dall’array @F

join " ",@F unisce gli elementi dell’array @F, usando uno spazio tra ogni elemento


Soluzione Python:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file

Ciò funzionerebbe se si utilizza Bash e si potrebbe usare il maggior numero di “x” come elementi che si desidera scartare e ignora più spazi se non sono sfuggiti.

 while read xb; do echo "$b"; done < filename 

Se non si desidera riformattare la parte della linea che non si taglia, la soluzione migliore che riesco a pensare è scritta nella mia risposta in:

Come stampare tutte le colonne dopo un numero particolare usando awk?

Elimina ciò che è prima del numero di campo specificato N e stampa tutto il resto della riga, incluso il numero di campo N e mantenendo la spaziatura originale (non riformatta). Non importa se la stringa del campo appare anche da qualche altra parte nella linea.

Definire una funzione:

 fromField () { awk -vm="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}' } 

E usalo in questo modo:

 $ echo " bat bi iru lau bost " | fromField 3 iru lau bost $ echo " bat bi iru lau bost " | fromField 2 bi iru lau bost 

L’output mantiene tutto, inclusi gli spazi finali

Nel tuo caso particolare:

 svn status | grep '\!' | fromField 2 > removedProjs 

Se il tuo file / stream non contiene caratteri di nuova riga nel mezzo delle linee (potresti usare un diverso separatore di record), puoi usare:

 awk -vm="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}' 

Il primo caso fallirà solo nei file / flussi che contengono il raro numero di caratteri esadecimali 1

Perl:

 @m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`; foreach $i (@m) { print "$i\n"; } 

Questa funzione di awk restituisce una sottostringa di $0 che include i campi begin alla end :

 function fields(begin, end, b, e, p, i) { b = 0; e = 0; p = 0; for (i = 1; i <= NF; ++i) { if (begin == i) { b = p; } p += length($i); e = p; if (end == i) { break; } p += length(FS); } return substr($0, b + 1, e - b); } 

Per ottenere tutto a partire dal campo 3:

 tail = fields(3); 

Per ottenere una sezione di $0 che copre i campi da 3 a 5:

 middle = fields(3, 5); 

b, e, p, i assurdità nella lista dei parametri di funzione è solo un modo awk di dichiarare variabili locali.

Voglio estendere le risposte proposte alla situazione in cui i campi sono delimitati da possibilmente diversi spazi bianchi – il motivo per cui l’OP non sta usando il cut suppongo.

So che l’OP ha chiesto di awk , ma un approccio sed avrebbe funzionato qui (esempio con colonne di stampa dal 5 ° all’ultimo):

  • approccio puro sed

     sed -r 's/^\s*(\S+\s+){4}//' somefile 

    Spiegazione:

    • s/// è usato il modo standard per eseguire la sostituzione
    • ^\s* corrisponde a qualsiasi spazio bianco consecutivo all’inizio della riga
    • \S+\s+ indica una colonna di dati (caratteri non di spaziatura seguiti da caratteri di spaziatura)
    • (){4} significa che il modello è ripetuto 4 volte.
  • sed e tagliare

     sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5- 

    semplicemente sostituendo gli spazi bianchi consecutivi con una singola scheda;

  • tr e cut: tr può anche essere usato per spremere caratteri consecutivi con l’opzione -s .

     tr -s [:blank:]  

Gli esempi di Awk sono complessi qui, ecco la semplice syntax della shell di Bash:

 command | while read -a cols; do echo ${cols[@]:1}; done 

Dove 1 è la tua colonna numero contando da 0.


Esempio

Dato questo contenuto di file ( in.txt ):

 c1 c1 c2 c1 c2 c3 c1 c2 c3 c4 c1 c2 c3 c4 c5 

ecco l’output:

 $ while read -a cols; do echo ${cols[@]:1}; done < in.txt c2 c2 c3 c2 c3 c4 c2 c3 c4 c5 

Non ero contento di nessuna delle soluzioni awk presentate qui perché volevo estrarre le prime poche colonne e poi stampare il resto, quindi mi sono rivolto invece a perl . Il seguente codice estrae le prime due colonne e visualizza il resto così com’è:

 echo -e "abcd\te\t\tf g" | \ perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;' 

Il vantaggio rispetto alla soluzione perl di Chris Koknat è che in realtà solo i primi n elementi sono separati dalla stringa di input; il resto della stringa non è affatto diviso e quindi rimane completamente intatto. Il mio esempio lo dimostra con un mix di spazi e tab.

Per modificare la quantità di colonne da estrarre, sostituisci il 3 nell’esempio con n + 1.

 ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }' 

da questa risposta non è male ma la spaziatura naturale è sparita.
Si prega quindi di confrontarlo con questo:

 ls -la | cut -d\ -f4- 

Allora vedresti la differenza.

Anche ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}' che si basa sulla risposta votata meglio finora non preserva la formattazione.

Quindi vorrei usare il seguente, e consente anche le colonne esplicite selettive all'inizio:

 ls -la | cut -d\ -f1,4- 

Nota che ogni spazio conta anche per le colonne, quindi per esempio nel seguito, le colonne 1 e 3 sono vuote, 2 è INFO e 4 è:

 $ echo " INFO 2014-10-11 10:16:19 main " | cut -d\ -f1,3 $ echo " INFO 2014-10-11 10:16:19 main " | cut -d\ -f2,4 INFO 2014-10-11 $ 

Se si desidera il testo formattato, concatenare i comandi con eco e utilizzare $ 0 per stampare l’ultimo campo.

Esempio:

 for i in {8..11}; do s1="$i" s2="str$i" s3="str with spaces $i" echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}' echo -en "$s3" | awk '{printf "|%-19s|\n", $0}' done 

stampe:

 | 8| str8|str with spaces 8 | | 9| str9|str with spaces 9 | | 10| str10|str with spaces 10 | | 11| str11|str with spaces 11 | 

A causa di un errore più sbagliato con 340 voti, ho perso solo 5 minuti della mia vita! Qualcuno ha provato questa risposta prima di mandare su questo? Apparentemente no. Completamente inutile.

Ho un registro dove dopo $ 5 con un indirizzo IP può essere più testo o nessun testo. Ho bisogno di tutto, dall’indirizzo IP alla fine della linea, se ci fosse qualcosa dopo $ 5. Nel mio caso, questo è in realtà con un programma awk, non un awk oneliner, quindi awk deve risolvere il problema. Quando provo a rimuovere i primi 4 campi usando la risposta più upvoted ma completamente sbagliata:

 echo " 7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}' 

sputa fuori risposta sbagliata e inutile (ho aggiunto [..] per dimostrare):

 [ 37.244.182.218 one two three] 

Ci sono persino alcune congetture per combinare il substr con questa risposta errata. Come quella complicazione è un miglioramento.

Invece, se le colonne hanno una larghezza fissa fino al punto di taglio e awk è necessario, la risposta corretta è:

 echo " 7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}' 

che produce l’output desiderato:

 [37.244.182.218 one two three]