Utilisation de awk pour imprimer toutes les colonnes de la nième à la dernière


310

Cette ligne a fonctionné jusqu'à ce que j'aie un espace dans le deuxième champ.

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

Existe-t-il un moyen pour que awk imprime tout en 2 $ ou plus? (3 $, 4 $ .. jusqu'à ce que nous n'ayons plus de colonnes?)

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


11
En grep | awkawk '/!/ { print $2 }'
passant

3
Unix "cut" est plus facile ...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic


@tripleee: Je suis tellement heureux que vous l'ayez mentionné - je suis frustré de le voir partout!
Graham Nicholls

Réponses:


490

imprimera tout sauf la toute première colonne:

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

affichera toutes les colonnes sauf deux:

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

93
gotcha: laisse un espace de premier plan pendant :(
raphinesse

5
j'aime l'approche pragmatique. pas besoin d'utiliser cat cependant, mettez simplement le nom du fichier après la commande awk.
kon

45
@raphinesse, vous pouvez résoudre ce problème avecawk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
Cela ne fonctionne pas avec les délimiteurs non blancs, les remplace par un espace.
Dejan

3
Pour les délimiteurs non blancs, vous pouvez spécifier le séparateur de champ de sortie (OFS), par exemple une virgule: awk -F, -vOFS=, '{$1=""; print $0}'vous vous retrouverez avec un délimiteur initial ( $1est toujours inclus, tout comme une chaîne vide). Vous pouvez retirer cela avec sedcependant:awk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt

99

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

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

-dspécifie le délimètre (espace) , -fspécifie la liste des colonnes (toutes commençant par la 2e)


Vous pouvez également utiliser "-b" pour spécifier la position (à partir du Nième caractère).
Dakatine

A noter, bien que cela effectue la même tâche que la awkversion, il y a des problèmes de mise en mémoire tampon de ligne avec cut, qui awkn'ont pas: stackoverflow.com/questions/14360640/…
sdaau

24
Agréable et simple, mais est livré avec une mise en garde: awktraite plusieurs caractères d'espace adjacents. comme un seul séparateur, tandis cutque non; aussi - bien que ce ne soit pas un problème dans le cas présent - cutn'accepte qu'un seul caractère littéral. comme délimiteur, alors que awkpermet une expression régulière.
mklement0

Sur la base de ceci: stackoverflow.com/a/39217130/8852408 , il est probable que cette solution n'est pas très efficace.
FcknGioconda

85

Vous pouvez utiliser une boucle for pour parcourir les champs d'impression de 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 tamponner les résultats:

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

Vous pouvez également utiliser printf:

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

J'ai donc essayé, mais je pense qu'il me manque quelque chose .. voici ce que j'ai fait svn status | grep '\!' | gawk '{for (i = 1; i <= $ NF; i ++) print $ i "";}'> suppriméProjs
Andy

Puisque print ajoute une nouvelle ligne, vous voudrez mettre les résultats en mémoire tampon. Voir mon montage.
VeeArr

1
J'aime mieux cette réponse car elle montre comment parcourir les champs.
Edward Falk

3
Si vous souhaitez que l'impression utilise un espace, modifiez le séparateur d'enregistrement de sortie: awk '{ORS = ""; for (i = 2; i <NF; i ++) print $ i} 'somefile
Christian Lescuyer

3
Il y aura toujours trop d'espaces. Cela fonctionne mieux: '{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'pas d'espaces de début ou de fin.
Marki

24
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é qu'elle commençait par un espace blanc avant d'imprimer la deuxième colonne (et le reste). Comme je n'ai qu'un point de réputation, je ne peux pas le commenter, 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.


2
Excellent, vous avez également supprimé le $ devant la variable out, ce qui est également important.
Alexis Wilke

15

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

Option 1

Une solution de découpe simple (ne fonctionne qu'avec des délimiteurs simples):

command | cut -d' ' -f3-

Option 2

Forcer un re-calc awk supprime parfois l'espace de tête (OFS) ajouté en supprimant les premiers champs (fonctionne avec certaines versions d'awk):

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

Option 3

L'impression de chaque champ formaté avec printfdonnera 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 changent tous les FS répétés entre les champs en OFS. Construisons quelques options qui ne le font pas.

Option 4 (recommandée)

Une boucle avec sub pour supprimer les champs et délimiteurs à l'avant.
Et en utilisant la valeur de FS au lieu de l'espace (qui pourrait être modifié).
Est -ce plus facile à transporter et ne déclenche pas un changement de FS à BSF: NOTE: Le ^[FS]*est d'accepter une entrée par des espaces.

$ 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'espaces supplémentaires (de début ou de fin) et de conserver les espaces existants en utilisant la fonction gensubde 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 en fonction d'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 sûr, dans ce 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é.

REMARQUE: [FS]* est utilisé pour autoriser les espaces de tête dans la ligne d'entrée.


13

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. La façon la 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, c'est l'espace, qui est également 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 jusqu'au dernier champ / colonne.

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


4
Problème, cela imprime chaque champ sur une ligne différente.
mveroone

rien ne vous empêche d'ajouter ceci à la fin :-) `| tr '\ n' '' '
koullislp

3
Un peu tard mais awk '{for (i = 5; i <= NF; i ++) {printf "% s", $ i}}'
plitter

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

lauhub a proposé ce correct, simple et rapide solution ici


7

Cela m'irritait tellement, je me suis assis et j'ai écrit un cutanalyseur de spécifications de champ semblable à celui testé avec GNU Awk 3.1.7.

Tout d'abord, créez 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 enregistrez. 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 la meilleure chose à faire (voir sinon Charger automatiquement une fonction utilisateur au démarrage avec awk? - Unix & 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 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);
}

On dirait que vous voulez utiliser cut, pasawk
roblogic

5

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

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

1
Bien, vous devez ajouter +après l'espace, car les champs peuvent être séparés par plus d'un espace ( awktraite plusieurs espaces adjacents comme un seul séparateur). Ignore également les awkespaces de début, vous devez donc commencer l'expression régulière avec ^[ ]*. Avec l'espace comme séparateur, vous pouvez même généraliser la solution; Par exemple, ce qui suit renvoie tout à partir du 3ème champ: awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'Cela devient cependant plus délicat avec des séparateurs de champs arbitraires.
mklement0

5

Est-ce que cela fonctionnerait?

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

Il laisse cependant un espace devant.


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

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


3

C'est ce que j'ai préféré de toutes les recommandations:

Impression de la 6e à 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"}'

2

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

Donc, si vous avez un espace dans une colonne, ce sera deux colonnes, mais vous pouvez le connecter avec n'importe quel délimiteur ou sans lui.


2

Solution Perl:

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

Ces options de ligne de commande sont utilisées:

  • -n boucle autour de chaque ligne du fichier d'entrée, n'imprime pas automatiquement chaque ligne

  • -l supprime les nouvelles lignes avant le traitement et les ajoute à nouveau après

  • -amode autosplit - divise les lignes d'entrée dans le tableau @F. Par défaut, la division sur un espace blanc

  • -e exécuter le code perl

splice @F,0,1 supprime proprement la colonne 0 du tableau @F

join " ",@F joint les éléments du tableau @F, en utilisant un espace entre chaque élément


Solution Python:

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


1

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:

Comment imprimer toutes les colonnes après un numéro particulier en utilisant awk?

Il coupe ce qui est avant le numéro de champ N donné et imprime tout le reste de la ligne, y compris le numéro de champ N et en conservant l'espacement d'origine (il ne reformate pas). Cela n'a pas d'importance si la chaîne du champ apparaît également ailleurs dans la ligne.

Définissez 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 pourriez utiliser 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 caractère hexadécimal rare numéro 1


0

Cela fonctionnerait si vous utilisez Bash et vous pouvez utiliser autant de «x» que d'éléments que vous souhaitez supprimer et il ignore plusieurs espaces s'ils ne sont pas échappés.

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
Cela ne répond pas à la question, qui généralise l'exigence d' imprimer de la Nième colonne à la fin .
roaima

0

Cette awkfonction renvoie une sous-chaîne $0qui inclut les 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 une section $0qui couvre les champs 3 à 5:

middle = fields(3, 5);

b, e, p, iun non-sens dans la liste des paramètres de fonction est juste un awkmoyen de déclarer des variables locales.


0

Je veux étendre les réponses proposées à la situation où les champs sont délimités par peut-être plusieurs espaces blancs - la raison pour laquelle l'OP n'utilise pas, cutje suppose.

Je sais que l'OP a posé des questions awk, mais une sedapproche fonctionnerait ici (exemple avec l'impression de colonnes du 5 au dernier):

  • approche sed pure

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

    Explication:

    • s/// est utilisé de manière standard pour effectuer la 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 cut

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

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

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

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

-1

Les exemples awk semblent complexes ici, voici la syntaxe simple du shell Bash:

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

1est votre n ième colonne à partir de 0.


Exemple

Compte tenu de ce contenu de fichier ( in.txt):

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

voici la sortie:

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

c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

-1

Je n'étais pas satisfait de l'une des awksolutions présentées ici parce que je voulais extraire les premières colonnes puis imprimer le reste, alors je me suis tourné vers la perlplace. Le code suivant extrait les deux premières colonnes et affiche le reste tel quel:

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

L'avantage par rapport à la perlsolution de Chris Koknat est qu'en réalité, seuls les n premiers éléments sont séparés de la chaîne d'entrée; le reste de la chaîne n'est pas du tout divisé et reste donc complètement intact. Mon exemple le démontre avec un mélange d'espaces et de tabulations.

Pour modifier le nombre de colonnes à extraire, remplacez le 3dans l'exemple par n + 1.


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

de cette réponse n'est pas mauvais mais l'espacement naturel a disparu.
Veuillez ensuite le comparer à celui-ci:

ls -la | cut -d\  -f4-

Vous verriez alors la différence.

Même ls -la | awk '{$1=$2=""; print}'ce qui est basé sur la réponse la mieux votée jusqu'à présent ne préserve pas la mise en forme.

Ainsi, j'utiliserais ce qui suit, et cela permet également des colonnes sélectives explicites au début:

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

Notez que chaque espace compte également pour les colonnes, donc par exemple dans ce qui suit, les colonnes 1 et 3 sont vides, 2 est INFO et 4 est:

$ 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

Si vous voulez du texte formaté, enchaînez vos commandes avec écho et utilisez $ 0 pour imprimer le dernier champ.

Exemple:

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

Tirages:

|  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

À cause d'une mauvaise réponse la plus votée avec 340 votes, je viens de perdre 5 minutes de ma vie! Quelqu'un at-il essayé cette réponse avant de voter pour cela? Apparemment non. Complètement inutile.

J'ai un journal où après 5 $ avec une adresse IP peut être plus de texte ou pas de texte. J'ai besoin de tout, de l'adresse IP à la fin de la ligne, s'il y a quelque chose après 5 $. Dans mon cas, il s'agit en fait d'un programme awk, pas d'un awel oneliner, donc awk doit résoudre le problème. Lorsque j'essaie de supprimer les 4 premiers champs en utilisant la réponse la plus élevée mais complètement incorrecte:

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

il crache une réponse fausse et inutile (j'ai ajouté [..] pour le démontrer):

[    37.244.182.218 one two three]

Il y a même quelques suggestions pour combiner substr avec cette mauvaise réponse. Comme cette complication est une amélioration.

Au lieu de cela, si les colonnes ont une largeur fixe jusqu'à ce que le point de coupure et awk soient nécessaires, la bonne réponse est:

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

qui produit la sortie souhaitée:

[37.244.182.218 one two three]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.