Usare awk per stampare tutte le colonne dall'ennesimo all'ultimo


310

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 avere awk stampare tutto in $ 2 o superiore? ($ 3, $ 4 .. fino a quando non avremo più colonne?)

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


11
A parte questo, grep | awkè un antipasto - vuoiawk '/!/ { print $2 }'
— tripleee,

3
Unix "cut" è più facile ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
— roblogic

Possibile duplicato del resto
— acm

@tripleee: sono così felice che tu l'abbia menzionato - sono frustrato nel vederlo ovunque!
— Graham Nicholls,

Risposte:


490

stamperà tutte tranne la prima colonna:

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

stamperà tutte tranne le prime due colonne:

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

93
gotcha: lascia uno spazio principale penzoloni :(
— raphinesse

5
mi piace l'approccio pragmatico. non è necessario usare cat, basta inserire il nome file dopo il comando awk.
— kon,

45
@raphinesse puoi risolverlo conawk '{$1=""; print substr($0,2)}' input_filename > output_filename
— themiurgo,

6
Questo non funziona con i delimitatori non bianchi, li sostituisce con uno spazio.
— Dejan,

3
Per i delimitatori di spazi non bianchi, è possibile specificare il separatore di campo di output (OFS), ad esempio una virgola: awk -F, -vOFS=, '{$1=""; print $0}'si finirà con un delimitatore iniziale ( $1è ancora incluso, proprio come una stringa vuota). Puoi sedawk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
— rimuoverlo

99

C'è una domanda duplicata con una risposta più semplice usando cut:

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

-dspecifica il delimitatore (spazio) , -fspecifica l'elenco di colonne (tutte a partire dal 2 °)


Puoi anche usare "-b" per specificare la posizione (dall'ennesimo carattere in poi).
— Dakatine,

Come nota, anche se questo esegue la stessa operazione come la awkversione, ci sono problemi di buffering linea con cut, che awknon ha: stackoverflow.com/questions/14360640/...
— sdaau

24
Bello e semplice, ma viene fornito con un avvertimento: awktratta più caratteri spaziali adiacenti. come un singolo separatore, mentre cutnon lo fa; inoltre, sebbene questo non sia un problema nel caso in esame, cutaccetta solo un singolo carattere letterale. come delimitatore, mentre awkconsente una regex.
— mklement0

Sulla base di questo: stackoverflow.com/a/39217130/8852408 , è probabile che questa soluzione non sia molto efficiente.
— FcknGioconda,

85

È possibile utilizzare un ciclo for per scorrere 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 bufferizzare i risultati:

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

In alternativa, utilizzare printf:

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

Quindi ho provato questo, ma penso che mi manchi qualcosa .. ecco cosa ho fatto svn status | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> rimossoProjs
— Andy

Poiché la stampa aggiunge una nuova riga, ti consigliamo di bufferizzare i risultati. Vedi la mia modifica.
— VeeArr,

1
Mi piace meglio questa risposta perché mostra come scorrere i campi.
— Edward Falk,

3
Se si desidera che la stampa utilizzi uno spazio, modificare il separatore del record di output: awk '{ORS = ""; per (i = 2; i <NF; i ++) print $ i} 'somefile
— Christian Lescuyer

3
Ci saranno sempre degli spazi troppo. Funziona meglio: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'nessuno spazio iniziale o finale.
— Marki,

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

La mia risposta si basa su quella di VeeArr , ma ho notato che è iniziata con uno spazio bianco prima che la seconda colonna (e il resto). Dato che ho solo 1 punto reputazione, non posso commentarlo, quindi ecco 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.


2
Eccellente, hai rimosso anche $ davanti alla variabile out che è importante.
— Alexis Wilke,

15

Molte soluzioni con awk lasciano 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 nuovo calcolo di awk a volte rimuove lo spazio iniziale aggiunto (OFS) rimasto 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 printfdarà un maggiore controllo:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=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 (consigliata)

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 a OFS: NOTA: L' ^[FS]*è di accettare un ingresso 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

È del tutto possibile creare una soluzione che non aggiunga spazi extra (iniziali o finali) e preservare spazi bianchi esistenti usando la funzione gensubdi GNU awk, in quanto:

$ echo '    1    2  3     4   5   6 7     8  ' |
  awk -v n=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 -v n=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, 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 spazi iniziali nella riga di input.


13

Personalmente ho provato tutte le risposte di cui sopra, ma la maggior parte di esse era un po 'complessa o semplicemente non corretta. 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 da usare per awk. 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 dell'ennesimo campo. Pertanto stampa $ i stamperà il campo / colonna corrente in base al conteggio dei cicli.


4
Problema, che stampa ogni campo su una riga diversa.
— mveroone,

nulla ti impedisce di aggiungere questo alla fine :-) `| tr '\ n' '' `
— koullislp,

3
Un po 'in ritardo ma awk' {for (i = 5; i <= NF; i ++) {printf "% s", $ i}} '
— plitter

8
awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

lauhub ha proposto questa soluzione corretta, semplice e veloce qui


7

Questo mi ha irritato così tanto, mi sono seduto e ho scritto un cutanalizzatore di specifiche di campo simile al test, testato con GNU Awk 3.1.7.

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

sudo nano /usr/share/awk/pfcut

Quindi, incolla lo script seguente e salva. Dopodiché, ecco come appare l'uso:

$ 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 digitare 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 es. con:

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

... allora 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 pfcutscript:

# 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 e.g. `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: "a-b"
    # 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);
}

Sembra che tu voglia usarlo cut, nonawk
— roblogic,

5

Stampa di colonne a partire dal n. 2 (all'inizio l'output non avrà spazi finali):

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

1
Bello, anche se dovresti aggiungere +dopo lo spazio, poiché i campi possono essere separati da più di 1 spazio ( awkconsidera più spazi adiacenti come un singolo separatore). Inoltre, awkignorerà gli spazi iniziali, quindi dovresti iniziare la regex con ^[ ]*. Con lo spazio come separatore potresti persino generalizzare la soluzione; ad esempio, quanto segue restituisce tutto dal 3 ° campo: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Tuttavia, diventa più complicato con separatori di campo arbitrari.
— mklement0

5

Funzionerebbe?

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

Tuttavia, lascia un po 'di spazio davanti.


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

questo usa awk per stampare tutto tranne l'ultimo campo


3

Questo è quello che ho preferito da tutte le raccomandazioni:

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"}'

2

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 saranno due colonne, ma puoi collegarlo con qualsiasi delimitatore o senza di esso.


2

Soluzione Perl:

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

Vengono utilizzate queste opzioni della riga di comando:

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

  • -l rimuove le nuove righe prima dell'elaborazione e le aggiunge nuovamente in seguito

  • -amodalità autosplit - divide le linee di input nell'array @F. L'impostazione predefinita è la divisione in spazi bianchi

  • -e eseguire 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


1

Se non vuoi riformattare la parte della linea che non tagli, la migliore soluzione a cui riesco a pensare è scritta nella mia risposta in:

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

Taglia ciò che precede il dato numero di campo N e stampa tutto il resto della linea, incluso il campo numero 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 -v m="\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, compresi 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 righe (potresti utilizzare un altro separatore di record), puoi utilizzare:

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

Il primo caso fallirà solo nei file / stream che contengono il raro numero esadecimale numero 1


0

Funzionerebbe se stai usando Bash e potresti usare quante 'x' quanti elementi vuoi scartare e ignora più spazi se non sono sfuggiti.

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

0

Perl:

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

}

1
Questo non risponde alla domanda, che generalizza il requisito di stampare dall'ennesima colonna fino alla fine .
— roaima,

0

Questa awkfunzione restituisce una sottostringa $0che include campi da begina 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 $0che copre i campi da 3 a 5:

middle = fields(3, 5);

b, e, p, ile sciocchezze nell'elenco dei parametri delle funzioni sono solo un awkmodo per dichiarare le variabili locali.


0

Voglio estendere le risposte proposte alla situazione in cui i campi sono delimitati da probabilmente diversi spazi bianchi , il motivo per cui il PO non sta usandocut suppongo che .

So che l'OP ha chiesto awk, ma un sedapproccio funzionerebbe qui (esempio con la stampa di colonne dal 5 all'ultimo):

  • approccio sed puro

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

    Spiegazione:

    • s/// viene utilizzato 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 bianchi seguiti da caratteri bianchi)
    • (){4} significa che il motivo viene 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 and cut: trpuò anche essere usato per stringere caratteri consecutivi con l' -sopzione.

    tr -s [:blank:] <somefile | cut -d' ' -f5-

-1

Qui alcuni esempi sembrano complessi, ecco la semplice sintassi della shell Bash:

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

Dove 1è il tuo n ° di conteggio della colonna 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

-1

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

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

Il vantaggio rispetto alla perlsoluzione 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 schede.

Per modificare la quantità di colonne da estrarre, sostituire 3l'esempio con n + 1.


-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-

Quindi vedresti la differenza.

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

Pertanto, vorrei utilizzare quanto segue e consentirebbe anche esplicite colonne selettive all'inizio:

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

Nota che ogni spazio conta anche per le colonne, quindi ad 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
$

-1

Se si desidera un testo formattato, concatenare i comandi con echo 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 |

-9

A causa di una risposta più sbagliata con 340 voti, ho perso 5 minuti della mia vita! Qualcuno ha provato questa risposta prima di votare questo? Apparentemente no. Completamente inutile.

Ho un registro in cui dopo $ 5 con un indirizzo IP può essere più testo o nessun testo. Ho bisogno di tutto, dall'indirizzo IP alla fine della riga, se ci fosse qualcosa dopo $ 5. Nel mio caso, questo è in realtà con un programma awk, non un onkiner Awk, quindi Awk deve risolvere il problema. Quando provo a rimuovere i primi 4 campi usando la risposta più votata 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 risposta sbagliata e inutile (ho aggiunto [..] per dimostrare):

[    37.244.182.218 one two three]

Ci sono anche alcuni suggerimenti per combinare substr con questa risposta sbagliata. Come quella complicazione è un miglioramento.

Invece, se le colonne hanno una larghezza fissa fino a quando non sono necessari il punto di taglio e awk, 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]
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.