web-dev-qa-db-fra.com

Imprimer toutes les colonnes sauf les trois premières

Trop lourd:

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things
107
hhh

Une solution qui n'ajoute pas d'avance ou de fin supplémentaire espaces blancs :

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_O propose une amélioration élégante en utilisant l'opérateur ternaire NF?ORS:OFS

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

EdMorton donne une solution en préservant les espaces blancs d'origine entre les champs:

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebra fournit également deux solutions géniales:
(ces solutions préservent même les espaces de fin de la chaîne d'origine)

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

La solution donnée par larsr _ dans les commentaires est presque correcte:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

Voici la version fixe et paramétrée de la solution larsr :

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

Toutes les autres réponses avant septembre 2013 sont gentilles mais ajoutent des espaces supplémentaires:

47
olibre
awk '{for(i=1;i<4;i++) $i="";print}' file
74
jiju

utiliser la coupe

$ cut -f4-13 file

ou si vous insistez sur awk et 13 $ est le dernier champ

$ awk '{$1=$2=$3="";print}' file

else

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file
69
ghostdog74

Essaye ça:

awk '{ $1=""; $2=""; $3=""; print $0 }'
38
lhf

La méthode correcte consiste à utiliser un intervalle RE, car il vous permet simplement d’indiquer le nombre de champs à ignorer et de conserver l’espacement entre les champs pour les champs restants.

par exemple. Ignorer les 3 premiers champs sans affecter l'espacement entre les champs restants, compte tenu du format d'entrée que nous semblons discuter dans cette question, c'est simplement:

$ echo '1   2 3 4   5    6' |
awk '{sub(/([^ ]+ +){3}/,"")}1'
4   5    6

Si vous souhaitez prendre en charge les espaces de début et les espaces non vides, mais encore une fois avec le système de fichiers par défaut, alors:

$ echo '  1   2 3 4   5    6' |
awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
4   5    6

Si vous avez un FS qui est une ER que vous ne pouvez pas nier dans un jeu de caractères, vous pouvez le convertir d'abord en un seul caractère (RS est idéal s'il s'agit d'un seul caractère, car un RS ne peut pas apparaître dans un champ, sinon SUBSEP), puis appliquez la substitution d’intervalle RE, puis convertissez-la en OFS. par exemple. si des chaînes de "." sont séparées les champs:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
4 5 6

Évidemment, si OFS est un caractère unique ET qu'il ne peut pas apparaître dans les champs de saisie, vous pouvez le réduire à:

$ echo '1...2.3.4...5....6' |
awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
4 5 6

Ensuite, vous rencontrez le même problème que pour toutes les solutions basées sur des boucles qui réaffectent les champs: les FS sont convertis en OFS. Si cela pose un problème, vous devez examiner la fonction patsplit () de awks de GNU.

24
Ed Morton

Presque toutes les réponses ajoutent actuellement des espaces de début, de fin ou un autre problème de séparation. Pour sélectionner dans le quatrième champ où le séparateur est un espace et le séparateur de sortie est un espace unique utilisant awk serait:

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

Pour paramétrer le champ de départ, vous pouvez faire:

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

Et aussi le champ de fin:

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file
10
Chris Seymour
awk '{$1=$2=$3="";$0=$0;$1=$1}1'

Contribution

1 2 3 4 5 6 7

Sortie

4 5 6 7
6
user4386814
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'
4
Vetsin

Les options 1 à 3 ont des problèmes avec plusieurs espaces (mais sont simples) . C’est la raison pour laquelle nous avons développé les options 4 et 5, qui traitent plusieurs espaces sans aucun problème ..__ Bien sûr, si les options 4 ou 5 sont: utilisé avec n=0, les deux conserveront les espaces les plus importants car n=0 signifie pas de division.

Option 1

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

$ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
4 5 6 7 8

Option 2

Forcer un awk re-calcule parfois le problème (fonctionne avec certaines versions de awk) des espaces de début ajoutés:

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

Option 3

L'impression de chaque champ formaté avec printf donnera plus de contrôle:

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

Cependant, toutes les réponses précédentes modifient tous les FS entre champs en OFS. Construisons quelques solutions à cela.

Option 4

Une boucle avec sub pour supprimer les champs et les délimiteurs est plus portable et ne déclenche pas le changement de FS en OFS:

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

NOTE: Le "^ [" FS "] *" doit accepter une entrée comportant des espaces.

Option 5

Il est tout à fait possible de créer une solution qui n’ajoute pas d’espace de début ou de fin, et qui préserve les espaces existants à l’aide de la fonction gensub de GNU awk, comme suit:

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

Il peut également être utilisé pour échanger une liste de champs avec un nombre n:

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

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

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

3
user2350426

Je n'arrive pas à croire que personne n'ait offert à Shell:

while read -r a b c d; do echo "$d"; done < file
3
glenn jackman

Une autre façon d'éviter d'utiliser l'instruction print:

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

Dans awk quand une condition est vraie, l'impression est l'action par défaut.

3
klashxx

Cut a un indicateur --complement qui permet de supprimer des colonnes facilement (et rapidement). La syntaxe obtenue est analogue à ce que vous voulez faire - rendre la solution plus facile à lire/à comprendre. Complément fonctionne également dans le cas où vous souhaitez supprimer des colonnes non contiguës. 

$ foo='1 2 3 %s 5 6 7'
$ echo "$foo" | cut --complement -d' ' -f1-3
%s 5 6 7
$
1
Michael Back

Comme j'étais contrarié par la première réponse fort élevée, mais erronée, j'ai trouvé suffisamment de réponses pour y écrire une réponse. Ici, les mauvaises réponses sont signalées comme telles. Je n'aime pas les solutions proposées car je ne vois aucune raison de rendre la réponse aussi complexe.

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 quoi que ce soit après 5 $. Dans mon cas, il s’agit en réalité d’un programme awk, et non d’un programme awk, donc awk doit résoudre le problème. Lorsque j'essaie de supprimer les 4 premiers champs en utilisant le vieux Nice, il semble être la réponse la plus élevée, mais tout à fait fausse:

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

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

[    37.244.182.218 one two three]

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

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

qui produit la sortie désirée:

[37.244.182.218 one two three]
0
Pila

Pour moi, la solution la plus compacte et la plus conforme à la demande est 

$ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
$ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'

Et si vous avez plus de lignes à traiter comme par exemple le fichier foo.txt , n'oubliez pas de remettre i à 0:

$ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt

Merci votre forum.

0
user8008888

Utilisez couper:

cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>

exemple: Si vous avez file1 contenant: car.is.Nice.equal.bmw

Exécuter: cut -d . -f1,3 file1 imprimera car.is.Nice

0
zayed