Comment utiliser une commande shell pour afficher uniquement la première colonne et la dernière colonne dans un fichier texte?

30

J'ai besoin d'aide pour comprendre comment utiliser la commande sed pour afficher uniquement la première et la dernière colonne d'un fichier texte. Voici ce que j'ai jusqu'à présent pour la colonne 1:

cat logfile | sed 's/\|/ /'|awk '{print $1}'

Ma faible tentative pour que la dernière colonne s'affiche également était:

cat logfile | sed 's/\|/ /'|awk '{print $1}{print $8}'

Cependant, cela prend la première colonne et la dernière colonne et les fusionne dans une liste. Existe-t-il un moyen d'imprimer clairement la première colonne et les dernières colonnes avec les commandes sed et awk?

Exemple d'entrée:

foo|dog|cat|mouse|lion|ox|tiger|bar
user70573
la source
5
Veuillez fournir un exemple d'entrée.
jasonwryan

Réponses:

51

Presque là. Placez simplement les deux références de colonne l'une à côté de l'autre.

cat logfile | sed 's/|/ /' | awk '{print $1, $8}'

Notez également que vous n'avez pas besoin catici.

sed 's/|/ /' logfile | awk '{print $1, $8}'

Notez également que vous pouvez dire awkque les séparateurs de colonnes sont |, au lieu de blancs, vous n'avez donc pas besoin non sedplus.

awk -F '|' '{print $1, $8}' logfile

Selon les suggestions de Caleb , si vous voulez une solution qui génère toujours le dernier champ, même s'il n'y en a pas exactement huit, vous pouvez l'utiliser $NF.

awk -F '|' '{print $1, $NF}' logfile

De plus, si vous souhaitez que la sortie conserve les |séparateurs, au lieu d'utiliser un espace, vous pouvez spécifier les séparateurs de champ de sortie. Malheureusement, c'est un peu plus maladroit que de simplement utiliser le -Fdrapeau, mais voici trois approches.

  • Vous pouvez affecter les séparateurs de champs d'entrée et de sortie en eux- awkmêmes, dans le bloc BEGIN.

    awk 'BEGIN {FS = OFS = "|"} {print $1, $8}' logfile
  • Vous pouvez affecter ces variables lors d'un appel à awkpartir de la ligne de commande, via l' -vindicateur.

    awk -v 'FS=|' -v 'OFS=|' '{print $1, $8}' logfile
  • ou simplement:

    awk -F '|' '{print $1 "|" $8}' logfile
Sparhawk
la source
4
Bon travail décomposant comment ce problème peut être simplifié. Vous pouvez ajouter une note sur la façon de l'utiliser |comme séparateur de sortie au lieu de l'espace par défaut pour la concaténation de chaînes. Vous pouvez également expliquer l'utilisation $NFau lieu du codage en dur $8pour obtenir la dernière colonne.
Caleb
12

Remplacez simplement du premier au dernier |par un |(ou un espace si vous préférez):

sed 's/|.*|/|/'

Notez que bien qu'il n'y ait pas d' sedimplémentation où |est spécial (tant que les expressions régulières étendues ne sont pas activées via -Eou -rdans certaines implémentations), \|elle-même est spéciale dans certains comme GNU sed. Vous ne devez donc pas vous échapper |si vous voulez qu'il corresponde au |personnage.

Si le remplacement par de l'espace et si l'entrée peut déjà contenir des lignes avec une seule |, alors, vous devrez traiter cela spécialement comme |.*|ne correspondra pas à ceux-ci. Cela pourrait être:

sed 's/|\(.*|\)\{0,1\}/ /'

(c'est-à-dire rendre la .*|pièce facultative) Ou:

sed 's/|.*|/ /;s/|/ /'

ou:

sed 's/\([^|]*\).*|/\1 /'

Si vous voulez les premier et huitième champs quel que soit le nombre de champs dans l'entrée, alors c'est juste:

cut -d'|' -f1,8


(tous ceux-ci fonctionneraient avec n'importe quel utilitaire compatible POSIX en supposant que l'entrée forme du texte valide (en particulier, sedceux-ci ne fonctionneront généralement pas si l'entrée contient des octets ou des séquences d'octets qui ne forment pas de caractères valides dans les paramètres régionaux actuels comme par exemple printf 'unix|St\351phane|Chazelas\n' | sed 's/|.*|/|/'dans une locale UTF-8)).

Stéphane Chazelas
la source
11

Vous utilisez awkquand même:

awk '{ print $1, $NF }' file
jasonwryan
la source
2
N'auriez-vous pas besoin de spécifier le séparateur de champ de saisie (car dans ce cas, il semble être |plutôt cet espace) avec -F\|ou similaire? Et s'il voulait utiliser le même délimiteur pour la sortie?
Caleb
@Caleb Probablement: j'attendais que l'OP confirme à quoi ressemblait exactement l'entrée, plutôt que d'essayer de deviner sur la base des exemples non fonctionnels ...
jasonwryan
1
Notez que cela suppose que l'entrée contient au moins 2 champs.
Stéphane Chazelas
@ StéphaneChazelas OP a clairement indiqué dans le code qu'il a toujours huit champs.
michaelb958
3
@ michaelb958 Je pense que "clairement" surestime le cas, juste un peu :)
jasonwryan
4

Si vous vous retrouvez sans gêne ni séduction, vous pouvez obtenir la même chose avec coreutils:

paste <(           cut -d'|' -f1  file) \ 
      <(rev file | cut -d'|' -f1 | rev)
Thor
la source
cutest plus propre et plus compact que awk / sed lorsque vous êtes juste intéressé par la première colonne, ou si les délimètres sont fixes (c'est-à-dire pas un nombre variable d'espaces).
Sridhar Sarnobat
2

Il semble que vous essayez d'obtenir les premier et dernier champs de texte délimités par |.

J'ai supposé que votre fichier journal contient le texte ci-dessous,

foo|dog|cat|mouse|lion|ox|tiger|bar
bar|dog|cat|mouse|lion|ox|tiger|foo

Et vous voulez la sortie comme,

foo bar
bar foo

Si oui, alors voici la commande pour la vôtre

Grâce à GNU sed,

sed -r 's~^([^|]*).*\|(.*)$~\1 \2~' file

Exemple:

$ echo 'foo|dog|cat|mouse|lion|ox|tiger|bar' | sed -r 's~^([^|]*).*\|(.*)$~\1 \2~'
foo bar
Avinash Raj
la source
Les colonnes ne sont pas délimitées par un tuyau | mais ils sont en colonnes, je suis intéressé par l'utilisation de sed mais pas par la commande awk comme vous l'avez fait dans votre commande: sed -r's ~ ^ ([^ |] *). * \ | (. *) $ ~ \ 1 \ 2 ~ 'file
user70573
"Les colonnes ne sont pas délimitées par un tuyau | mais elles sont en colonnes", vous voulez dire que les colonnes sont séparées par des espaces?
Avinash Raj
Un échantillon d'entrée et une sortie seraient mieux.
Avinash Raj
1

Vous devriez probablement le faire avec sed- je le ferais de toute façon - mais, juste parce que personne n'a encore écrit celui-ci:

while IFS=\| read col1 cols
do  printf %10s%-s\\n "$col1 |" " ${cols##*|}"
done <<\INPUT
foo|dog|cat|mouse|lion|ox|tiger|bar
INPUT

SORTIE

     foo | bar
mikeserv
la source