pouvons-nous imprimer le dernier mot de chaque ligne sous linux en utilisant la commande sed?

9

supposons, s'il y a un fichier composé des lignes suivantes, si elles sont

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 yy46y6y

La sortie doit ressembler à:

66

y6y46y

y5y

y66uyuyy

y46y6y

J'ai essayé la commande sed 's/.* //g'filename et plusieurs autres sedcommandes, mais cela ne fonctionne pas.

Puis-je savoir quelle est la sedcommande exacte ?

Rajeev Nukala
la source
Est-ce un must à utiliser sed?
coffeMug

Réponses:

8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Cela imprimerait toujours une ligne vide pour chaque ligne vierge. Pour l'éviter:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'
Stéphane Chazelas
la source
Alternative expression unique: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
jimmij
@jimmij - celui-ci ne fonctionne pas si la dernière séquence non vide est également la première et qu'il n'y a pas de blancs la précédant. En outre, vous pourriez aussi bien faire juste .*à la queue, probablement - vous excluez tout sauf les blancs de toute façon avec .*[^[:blank:]].
mikeserv
6

La awkvariable $NFest le dernier champ de chaque enregistrement ; vous pouvez l'utiliser pour imprimer uniquement les derniers champs de votre fichier comme ceci:

awk '{print $NF}' file
jasonwryan
la source
4

Tu peux essayer :

  • sed 's/.* //'
  • awk '{print $NF}'
Uriel
la source
4

Tu y es presque. Précisez simplement le dernier mot:

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

Ce qu'il fait:

  1. '^. *' supprime tout ce qui se trouve au début de la ligne et tous les espaces.
  2. '\ (...) \' correspond à un modèle et le renvoie comme \ 1.
  3. '[^]' correspond à tout ce qui ne contient pas d'espace.

(Modifié pour ajouter une meilleure solution. Merci Hildred!)

Ton continu
la source
1
Voici une expression plus courte: sed -r 's/.* ([^ ]+)/\1/g'si les expressions régulières étendues sont autorisées, ce qui est généralement le cas.
mkalkov
Version plus courte, en remplaçant ce que vous ne voulez pas garder plutôt que ce que vous voulez garder:sed 's/.* //'
Uriel
2

Vous pouvez utiliser un modèle adéquat de grepau lieu de sed, par exemple:

grep -o "[a-Z0-9]*$"

Dans cet exemple, le [...]contient des plages de caractères considérées comme appropriées pour un "mot" (alphanumériques dans ce cas, d'autres symboles pourraient être ajoutés, dont certains doivent être échappés).

Dalker
la source
2
Cela suppose qu'il n'y a pas de blanc à la fin de la ligne. a-Zcar une plage n'a pas beaucoup de sens, même dans les environnements locaux basés sur ASCII. Notez qu'il -os'agit d'une extension GNU.
Stéphane Chazelas
0

Si vous qualifiez un mot pour signifier n'importe quelle séquence de 1 ou plusieurs caractères non vides, la réponse est certainement oui, et cela se fait très simplement également. En effet, [[:blank:]]*et [^[:blank:]]*sont des compléments booléens et - à condition que tous les caractères d'une chaîne soient complets - [[:blank:]]*U [^[:blank:]]*peut décrire n'importe quelle chaîne possible de la même manière .*.

Si un caractère incomplet ou une séquence d'octets non valide existe dans une chaîne, ni l'un ni l'autre ne peut le décrire correctement, comme cela peut parfois se produire lors de l'interprétation d'une chaîne dans le mauvais codage. Pour garantir un caractère complet par octet dans n'importe quelle chaîne, les paramètres régionaux C peuvent être forcés comme:

LC_ALL=C sed ...

... ce qui éviterait tout problème de description de la chaîne de la tête à la queue avec un modèle tout compris tel que .*ou([ ]*[^ ]*)*

Un modèle entièrement complémentaire peut répéter autant de fois que nécessaire de gauche à droite la longueur de n'importe quelle chaîne pour atterrir sur la dernière occurrence possible sans interruption du modèle. C'est, définitivement, un langage régulier.

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

AVANT:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

Ces deux versions imprimeront toujours des lignes vierges, et c'est parce que l' *étoile Kleene correspond à zéro ou plusieurs occurrences d'un motif. Il correspond d'abord à zéro ou plusieurs caractères non vides, puis à zéro ou plusieurs caractères vides, puis à zéro ou plusieurs occurrences des correspondances groupées jusqu'à ce qu'il corresponde à la chaîne dans son intégralité.

Ayant fait correspondre tout cela, la magie opère dans le remplacement - les références retournées par les groupes \1et \2sont les dernières occurrences de chacun. Ainsi, lorsque le remplacement est effectué, toute la chaîne est remplacée par la dernière occurrence sur une ligne de zéro ou plusieurs caractères non vides - ou le sous-groupe \2.

Bien sûr, cela fonctionne pour toute chaîne possible - même une chaîne vide - ce qui signifie que les deux formulaires imprimeront des caractères de nouvelle ligne pour les lignes qui ne contiennent que des caractères vierges ou pas du tout. Pour gérer cela, vous pouvez faire quelques choses, mais rendons d'abord la classe de caractères un peu plus facile à taper:

b='[:blank:]'

Maintenant, pour imprimer uniquement si une ligne contient un ou plusieurs caractères non vides, vous pouvez faire:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

AVANT:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Cas BRE - la substitution est toujours effectuée et seuls les espaces de motif avec au moins un caractère restant sont imprimés.
  2. Cas ERE - la substitution n'est tentée que sur un espace modèle contenant au moins un caractère non vide.

L'une ou l'autre forme fonctionnera avec l'une ou l'autre méthode - tant que la syntaxe est correcte.

Le -ncommutateur désactive l'impression automatique de l'espace de motif et l' pindicateur de l' s///ubstitution ou des commandes d' /adresse /imprime ses résultats uniquement en cas de succès.

Cette même logique peut également être appliquée pour obtenir n'importe quelle {num}occurrence, comme:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

AVANT:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... où les numdeux expressions rationnelles peuvent être remplacées par un nombre pour imprimer uniquement l' {num}occurrence spécifiée d'une séquence de caractères non vides. Une forme légèrement différente est utilisée ici pour garantir que le nombre n'est pas faussé pour l'espace de début dans une chaîne.

Notez que le -Ecommutateur ERE sedest pris en charge dans les versions BSD et GNU, bien qu'il ne soit pas encore la syntaxe standard POSIX.

mikeserv
la source
Belles explications, bon hack, mais notez que cela ne fonctionnera pas avec les sed implémentations traditionnelles (comme Solaris / usr / bin / sed) et qu'il sera plus cher que l'approche la plus simple (épuise la mémoire avec des lignes d'entrée de plus de 25 caractères avec le sed_su3du toolchest par exemple Heirloom). Donc, bien que j'aime la réponse, je ne recommanderais pas cette approche.
Stéphane Chazelas
Ne semble pas fonctionner non plus dans FreeBSD.
Stéphane Chazelas
@ StéphaneChazelas - oui, la performance est vraiment horrible pour une chose comme ça, mais elle peut être très efficace pour sélectionner les occurrences numérotées. Et pour un cas de fin de ligne, s/.* \([^[:blank:]]\{1,\}\).*/\1/c'est beaucoup mieux, mais c'est plus difficile lorsque plusieurs lignes sont impliquées. L'autre jour, cependant, j'ai découvert que cela 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]peut assez efficacement étayer cela. Quoi qu'il en soit, tant qu'il n'y a pas d'erreur flagrante dans la logique, je suis heureux - je pensais juste que j'avais dû manquer quelque chose.
mikeserv
@ StéphaneChazelas - oh, et pour les plus vieux sed- c'est un peu bizarre - ça devrait être sain selon la norme. xrat dit ... Les développeurs standard considéraient le comportement historique commun, qui supportait "\n*", mais pas "\n\{min,max\}", "\(...\)*", ou "\(...\)\{min,max\}", comme un résultat non intentionnel d'une implémentation spécifique, et ils supportaient à la fois la duplication et les expressions d'intervalle après les sous-expressions et les références arrières.
mikeserv
@ StéphaneChazelas - Et la norme dit ... Si la sous-expression référencée par la référence arrière correspond à plus d'une chaîne en raison d'un astérisque ( '*' )ou d'une expression d'intervalle (voir point (5)), la référence arrière doit correspondre à la dernière (la plus à droite ) de ces chaînes. Je suis à peu près sûr d'avoir testé cela avec - minisedbien sûr, je testais quelque chose de bizarre avec minisedl'autre jour, de toute façon.
mikeserv
-1

Oui. La commande sed suivante supprime d'abord tous les espaces blancs de fin ( s/ *$//), puis tout ce qui se termine jusqu'au dernier espace blanc ( s/.* //) inclus . Il vaut probablement la peine de remplacer les espaces littéraux par [[:blank:]]pour capturer les tabulations et autres caractères de type espace.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc
mkalkov
la source
-1
cat file_name | rev | cut -f1 -d ' ' | rev
TEL QUEL
la source