web-dev-qa-db-fra.com

Utilisation de awk pour imprimer toutes les colonnes du nième au dernier

Cette ligne a fonctionné jusqu'à ce que je dispose d'espaces dans le deuxième champ.

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

y at-il un moyen d'avoir awk tout imprimer en $ 2 ou plus? (3 $, 4 $ .. jusqu'à ce que nous n'avons plus de colonnes?)

Je suppose que je devrais ajouter que je le fais dans un environnement Windows avec Cygwin.

249
Andy

imprimera tout sauf la première colonne:

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

affichera toutes les premières colonnes sauf deux:

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

Il y a une question en double avec un réponse plus simple using cut:

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

-d spécifie le délimiteur (espace), -f spécifie la liste des colonnes (commençant par la deuxième)

87
Joshua Goldberg

Vous pouvez utiliser une boucle for pour parcourir les champs d'impression $ 2 à $ NF (variable intégrée qui représente le nombre de champs sur la ligne).

Edit: Puisque "print" ajoute une nouvelle ligne, vous voudrez mettre les résultats en mémoire tampon:

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

Sinon, utilisez printf:

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

Ma réponse est basée sur celle de VeeArr , mais j'ai remarqué que cela commençait par un espace avant l'impression de la deuxième colonne (et du reste). Comme je n'ai qu'un point de réputation, je ne peux pas en parler, alors voici une nouvelle réponse:

commencez par "out" comme deuxième colonne, puis ajoutez toutes les autres colonnes (si elles existent). Cela va bien tant qu'il y a une deuxième colonne.

21
Wim

J'ai personnellement essayé toutes les réponses mentionnées ci-dessus, mais la plupart d'entre elles étaient un peu complexes ou tout simplement pas correctes. Le moyen le plus simple de le faire de mon point de vue est:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. Où -F "" définit le délimiteur à utiliser par awk. Dans mon cas, il s’agit de l’espace, qui est aussi le délimiteur par défaut pour awk. Cela signifie que -F "" peut être ignoré.

  2. Où NF définit le nombre total de champs/colonnes. Par conséquent, la boucle commencera du 4ème champ au dernier champ/colonne.

  3. Où $ N récupère la valeur du Nième champ. Par conséquent, print $ i imprimera le champ/la colonne en cours en fonction du nombre de boucles.

12
koullislp

La plupart des solutions avec awk laissent un espace. Les options ici évitent ce problème.

Option 1

Une solution de découpe simple (fonctionne uniquement avec des délimiteurs simples):

command | cut -d' ' -f3-

Option 2

Forcer un awk re-calcule parfois supprimer l’espace de début ajouté (OFS) en supprimant les premiers champs (fonctionne avec certaines versions de awk):

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

Option 3

Imprimer chaque champ formaté avec printf donnera plus de contrôle:

$ 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

Cependant, toutes les réponses précédentes modifient tous les FS répétés entre les champs en OFS. Construisons quelques options qui ne le font pas.

Option 4 (recommandé)

Une boucle avec sous pour supprimer les champs et les délimiteurs à l'avant.
Et en utilisant la valeur de FS au lieu de l’espace (qui peut être modifié).
Est plus portable et ne déclenche pas le changement de FS en OFS: REMARQUE: Le ^[FS]* permet d’accepter une entrée comportant des espaces en tête.

$ 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

Option 5

Il est tout à fait possible de créer une solution qui n’ajoute pas d’espace (avant ou après) supplémentaire et qui préserve les espaces existants à l’aide de la fonction gensub de GNU awk, comme ceci:

$ 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 

Il peut également être utilisé pour échanger un groupe de champs avec un nombre 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  !

Bien entendu, dans un tel cas, l'OFS est utilisé pour séparer les deux parties de la ligne et l'espace blanc de fin des champs est toujours imprimé.

NOTE: [FS]* est utilisé pour autoriser les espaces de début dans la ligne d'entrée.

8
user2350426

Cela m'énervait tellement, je me suis assis et j'ai écrit un analyseur de spécification de champ de type cut- like, testé avec GNU Awk 3.1.7. 

Commencez par créer un nouveau script de bibliothèque Awk appelé pfcut, avec par exemple:.

Sudo nano /usr/share/awk/pfcut

Ensuite, collez le script ci-dessous et sauvegardez. Après cela, voici à quoi ressemble l'utilisation:

$ 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

Pour éviter de taper tout cela, je suppose que le mieux que l'on puisse faire (voir sinon Charger automatiquement une fonction utilisateur au démarrage avec awk? - Unix et Linux Stack Exchange ) est d'ajouter un alias à ~/.bashrc; par exemple. avec:

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

... alors vous pouvez simplement appeler:

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

Voici la source du 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 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);
}
6
sdaau

Cela fonctionnerait-il?

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

Cela laisse cependant quelques blancs devant.

5
whaley

Impression des colonnes à partir de # 2 (la sortie n'aura aucun espace de fin au début)

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'
5
savvadia
echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

celui-ci utilise awk pour tout imprimer sauf le dernier champ

4
Kaushal Jha

Voici ce que j'ai préféré parmi toutes les recommandations:

Impression de la 6ème à la dernière colonne.

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

ou

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

lauhub a proposé cette solution correcte, simple et rapide ici

3
ajendrex

Si vous avez besoin de colonnes spécifiques imprimées avec un délimiteur arbitraire:

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

col # 3 col # 4

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

col # 3anythingcol # 4

Ainsi, si vous avez des espaces dans une colonne, il y aura deux colonnes, mais vous pouvez le connecter avec n'importe quel délimiteur ou sans. 

2
I159

Cela fonctionnerait si vous utilisiez Bash et que vous pouviez utiliser autant de «x» que d'éléments que vous souhaitez supprimer et que plusieurs espaces seraient ignorés s'ils ne sont pas protégés.

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

Cette fonction awk renvoie une sous-chaîne de $0 qui comprend des champs de begin à 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);
}

Pour tout obtenir à partir du champ 3:

tail = fields(3);

Pour obtenir la section de $0 qui couvre les champs 3 à 5:

middle = fields(3, 5);

b, e, p, i non-sens dans la liste des paramètres de fonction est simplement un moyen awk de déclarer des variables locales.

0
wonder.mice

Perl:

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

}
0
pkm

Si vous ne voulez pas reformater la partie de la ligne que vous ne coupez pas, la meilleure solution à laquelle je peux penser est écrite dans ma réponse en:

Comment imprimer toutes les colonnes après un nombre particulier en utilisant awk?

Il coupe ce qui se trouve avant le numéro de champ N donné et imprime le reste de la ligne, y compris le numéro de champ N et le maintien de l'espacement d'origine (il ne reformate pas). Peu importe que la chaîne du champ apparaisse également ailleurs dans la ligne.

Définir une fonction:

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

Et utilisez-le comme ceci:

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

La sortie conserve tout, y compris les espaces de fin

Dans votre cas particulier:

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

Si votre fichier/flux ne contient pas de caractères de nouvelle ligne au milieu des lignes (vous utilisez peut-être un séparateur d'enregistrement différent), vous pouvez utiliser:

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

Le premier cas échouera uniquement dans les fichiers/flux contenant le numéro de caractère hexadécimal rare 1

0
Robert Vila

Je souhaite étendre les réponses proposées à la situation où les champs sont délimités par éventuellement plusieurs espaces blancs - la raison pour laquelle le PO n'utilise pas cut je suppose.

Je sais que le PO a posé des questions sur awk, mais une approche sed fonctionnerait ici (exemple avec les colonnes imprimées du 5 au dernier):

  • approche pure sed

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

    Explication:

    • s/// est utilisé de manière standard pour effectuer une substitution
    • ^\s* correspond à tout espace blanc consécutif au début de la ligne
    • \S+\s+ signifie une colonne de données (caractères non blancs suivis de caractères blancs)
    • (){4} signifie que le motif est répété 4 fois.
  • sed et couper

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

    en remplaçant simplement les espaces consécutifs par un seul onglet;

  • tr et cut: tr peut également être utilisé pour presser des caractères consécutifs avec l'option -s.

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