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.