@MestreLion Plusieurs fois, les gens lisent une question pour trouver une solution à une variation d'un problème. Celui-ci commence avec la fausse prémisse qui cutsoutient quelque chose qu'il ne fait pas. Mais j'ai pensé que c'était utile, car cela oblige le lecteur à considérer un code plus facile à suivre. Je voulais un moyen rapide et simple à utiliser cutsans avoir besoin d'utiliser plusieurs syntaxes pour awk, grep, sed, etc. La revchose a fait l'affaire; très élégant, et quelque chose que je n'ai jamais envisagé (même s'il est maladroit pour d'autres situations). J'ai aussi aimé lire les autres approches des autres réponses.
Beejor
3
Est venu ici un problème réel: je veux trouver toutes les différentes extensions de fichier dans une arborescence source, pour mettre à jour un fichier .gitattributes avec. Il en find | cut -d. -f<last>va de même pour l'inclinaison naturelle
studog
Réponses:
680
Vous pouvez essayer quelque chose comme ça:
echo 'maps.google.com'| rev | cut -d'.'-f 1| rev
Explication
rev inverse "maps.google.com" pour être moc.elgoog.spam
cut utilise le point (c'est-à-dire «.») comme délimiteur et choisit le premier champ, qui est moc
enfin, nous l'inversons à nouveau pour obtenir com
Il n'utilise pas seulement cutmais c'est sans sedou awk.Alors qu'en pense OP?
Jayesh Bhoi
7
@tom OP a posé plus de questions que cela au cours des dernières heures. Sur la base de nos interactions avec l'OP, nous savons que awk / sed / etc. ne sont pas autorisés dans ses devoirs, mais aucune référence à rev n'a été faite. Donc ça valait le coup
zedfoxus
4
@zfus je vois. Pourrait vouloir en coller un autre revaprès.
Tom
17
double revgrand idéal!
Ford Guo
6
Génial, simple, parfait, merci aussi pour l'explication - pas assez de gens expliquant chaque étape dans de longues chaînes de commandes piped
Pete
128
Utilisez une extension de paramètre. C'est beaucoup plus efficace que n'importe quel type de commande externe, cut(ou grep) incluse.
data=foo,bar,baz,qux
last=${data##*,}
Voir BashFAQ # 100 pour une introduction à la manipulation de chaînes natives dans bash.
@ErwinWessels: Parce que bash est vraiment lent. Utilisez bash pour exécuter des pipelines, et non pour traiter des données en bloc. Je veux dire, c'est génial si vous avez déjà une ligne de texte dans une variable shell, ou si vous voulez faire while IFS= read -ra array_var; do :;done <(cmd)pour traiter quelques lignes. Mais pour un gros fichier, rev | cut | rev est probablement plus rapide! (Et bien sûr, awk sera plus rapide que ça.)
Peter Cordes
2
@PeterCordes, awk sera plus rapide pour un gros fichier, bien sûr, mais il faut pas mal d'entrée pour surmonter les coûts de démarrage à facteur constant. (Il existe également des shells - comme ksh93 - avec des performances plus proches de awk, où la syntaxe donnée dans cette réponse reste valide; bash est exceptionnellement lent, mais il n'est même pas proche de la seule option disponible).
Charles Duffy
1
Merci @PeterCordes; comme d'habitude, je suppose que chaque outil a ses cas d'utilisation.
Erwin Wessels
1
C'est de loin le moyen le plus rapide et le plus concis de réduire une seule variable dans un bashscript (en supposant que vous utilisez déjà un bashscript). Pas besoin d'appeler quoi que ce soit d'extérieur.
Ken Sharp
1
@Balmipour, ... cependant, revest spécifique au système d'exploitation que vous utilisez qui le fournit - il n'est pas standardisé sur tous les systèmes UNIX. Voir la liste des chapitres pour la section POSIX sur les commandes et les utilitaires - elle n'est pas là. Et ${var##prefix_pattern}n'est pas en fait spécifique à bash; il est dans la norme POSIX sh , voir la fin de la section 2.6.2 (lié), donc contrairement à rev, il est toujours disponible sur n'importe quel shell compatible.
Charles Duffy
89
Il n'est pas possible d'utiliser simplement cut. Voici un moyen d'utiliser grep:
Pour faire l'inverse, et trouver tout sauf le dernier champ faire:grep -o '^.*,'
Ariel
2
C'était particulièrement utile, car revajoutez un problème de caractères unicode multioctets dans mon cas.
Brice
3
J'essayais de le faire sur MinGW mais ma version grep ne prend pas en charge -o, j'ai donc utilisé sed 's/^.*,//'ce qui remplace tous les caractères jusqu'à et y compris la dernière virgule par une chaîne vide.
TamaMcGlinn
46
Sans awk? ... Mais c'est si simple avec awk:
echo 'maps.google.com'| awk -F.'{print $NF}'
AWK est un outil beaucoup plus puissant à avoir dans votre poche. -F si pour le séparateur de champs NF est le nombre de champs (représente également l'index du dernier)
C'est universel et cela fonctionne exactement comme prévu à chaque fois. Dans ce scénario, utiliser cutpour obtenir la sortie finale de l'OP revient à utiliser une cuillère pour "couper" le steak (jeu de mots voulu :)). awkest le couteau à steak.
Hickory420
3
Évitez une utilisation inutile de ce echoqui peut ralentir le script pour les longs fichiers awk -F. '{print $NF}' <<< 'maps.google.com'.
Anil_M
14
Il y a plusieurs façons. Vous pouvez également l'utiliser.
echo "Your string here"| tr ' ''\n'| tail -n1> here
De toute évidence, l'entrée d'espace vide pour la commande tr doit être remplacée par le délimiteur dont vous avez besoin.
En utilisant cette solution, le nombre de champs peut en effet être inconnu et varier de temps en temps. Cependant, comme la longueur de ligne ne doit pas dépasser les caractères ou champs LINE_MAX, y compris le caractère de nouvelle ligne, un nombre arbitraire de champs ne peut jamais faire partie comme condition réelle de cette solution.
Oui, une solution très idiote mais la seule qui répond aux critères je pense.
Agréable. Prenez le dernier '.' hors de "chaîne" et cela fonctionne.
Matt
2
J'adore quand tout le monde dit que quelque chose est impossible et que quelqu'un intervient avec une réponse qui fonctionne. Même si c'est en effet très idiot.
Beejor
On pourrait parcourir cut -f2-une boucle jusqu'à ce que la sortie ne change plus.
loa_in_
4
Si votre chaîne d'entrée ne contient pas de barres obliques, vous pouvez utiliser basenameet un sous-shell:
Cela n'utilise pas sedou awkmais il n'utilise pas non cutplus, donc je ne suis pas sûr si cela peut être considéré comme une réponse à la question telle qu'elle est formulée.
Cela ne fonctionne pas bien si vous traitez des chaînes d'entrée pouvant contenir des barres obliques. Une solution de contournement pour cette situation serait de remplacer la barre oblique par un autre caractère dont vous savez qu'il ne fait pas partie d'une chaîne d'entrée valide. Par exemple, le caractère pipe ( |) n'est pas non plus autorisé dans les noms de fichiers, donc cela fonctionnerait:
Vous avez besoin de guillemets doubles autour des arguments echopour que cela fonctionne de manière fiable et robuste. Voir stackoverflow.com/questions/10067266/…
tripleee
0
Si vous avez un fichier nommé filelist.txt qui est une liste de chemins tels que les suivants: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h
alors vous pouvez le faire: rev filelist.txt | cut -d "/" -f1 | tour
Ajout d'une approche à cette vieille question juste pour le plaisir:
$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info
$ cat tmp.sh # showing off the script to do the job#!/bin/bash
delim=';'while read -r line;dowhile[["$line"=~"$delim"]];do
line=$(cut -d"$delim"-f 2-<<<"$line")done
echo "$line"done< input.file
$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info
Outre bash, seule la coupe est utilisée. Eh bien, et l'écho, je suppose.
Meh, pourquoi ne pas supprimer complètement la coupe et utiliser uniquement bash ... x] while read -r line; do echo ${line/*;}; done <input.filedonne le même résultat.
Kaffe Myers
-1
J'ai réalisé que si nous nous contentions de nous assurer qu'un délimiteur de fin existe, cela fonctionne. Donc, dans mon cas, j'ai des délimiteurs de virgule et d'espaces. J'ajoute un espace à la fin;
cut
commande :)? pourquoi pas d'autres commandes Linux?sed
ouawk
:perl -pe 's/^.+\s+([^\s]+)$/$1/'
.cut
soutient quelque chose qu'il ne fait pas. Mais j'ai pensé que c'était utile, car cela oblige le lecteur à considérer un code plus facile à suivre. Je voulais un moyen rapide et simple à utilisercut
sans avoir besoin d'utiliser plusieurs syntaxes pourawk
,grep
,sed
, etc. Larev
chose a fait l'affaire; très élégant, et quelque chose que je n'ai jamais envisagé (même s'il est maladroit pour d'autres situations). J'ai aussi aimé lire les autres approches des autres réponses.find | cut -d. -f<last>
va de même pour l'inclinaison naturelleRéponses:
Vous pouvez essayer quelque chose comme ça:
Explication
rev
inverse "maps.google.com" pour êtremoc.elgoog.spam
cut
utilise le point (c'est-à-dire «.») comme délimiteur et choisit le premier champ, qui estmoc
com
la source
cut
mais c'est sanssed
ouawk
.Alors qu'en pense OP?rev
après.rev
grand idéal!Utilisez une extension de paramètre. C'est beaucoup plus efficace que n'importe quel type de commande externe,
cut
(ougrep
) incluse.Voir BashFAQ # 100 pour une introduction à la manipulation de chaînes natives dans bash.
la source
while IFS= read -ra array_var; do :;done <(cmd)
pour traiter quelques lignes. Mais pour un gros fichier, rev | cut | rev est probablement plus rapide! (Et bien sûr, awk sera plus rapide que ça.)bash
script (en supposant que vous utilisez déjà unbash
script). Pas besoin d'appeler quoi que ce soit d'extérieur.rev
est spécifique au système d'exploitation que vous utilisez qui le fournit - il n'est pas standardisé sur tous les systèmes UNIX. Voir la liste des chapitres pour la section POSIX sur les commandes et les utilitaires - elle n'est pas là. Et${var##prefix_pattern}
n'est pas en fait spécifique à bash; il est dans la norme POSIX sh , voir la fin de la section 2.6.2 (lié), donc contrairement àrev
, il est toujours disponible sur n'importe quel shell compatible.Il n'est pas possible d'utiliser simplement
cut
. Voici un moyen d'utilisergrep
:Remplacez la virgule pour les autres délimiteurs.
la source
grep -o '^.*,'
rev
ajoutez un problème de caractères unicode multioctets dans mon cas.sed 's/^.*,//'
ce qui remplace tous les caractères jusqu'à et y compris la dernière virgule par une chaîne vide.Sans awk? ... Mais c'est si simple avec awk:
AWK est un outil beaucoup plus puissant à avoir dans votre poche. -F si pour le séparateur de champs NF est le nombre de champs (représente également l'index du dernier)
la source
cut
pour obtenir la sortie finale de l'OP revient à utiliser une cuillère pour "couper" le steak (jeu de mots voulu :)).awk
est le couteau à steak.echo
qui peut ralentir le script pour les longs fichiersawk -F. '{print $NF}' <<< 'maps.google.com'
.Il y a plusieurs façons. Vous pouvez également l'utiliser.
De toute évidence, l'entrée d'espace vide pour la commande tr doit être remplacée par le délimiteur dont vous avez besoin.
la source
C'est la seule solution possible pour n'utiliser que du cut:
En utilisant cette solution, le nombre de champs peut en effet être inconnu et varier de temps en temps. Cependant, comme la longueur de ligne ne doit pas dépasser les caractères ou champs LINE_MAX, y compris le caractère de nouvelle ligne, un nombre arbitraire de champs ne peut jamais faire partie comme condition réelle de cette solution.
Oui, une solution très idiote mais la seule qui répond aux critères je pense.
la source
cut -f2-
une boucle jusqu'à ce que la sortie ne change plus.Si votre chaîne d'entrée ne contient pas de barres obliques, vous pouvez utiliser
basename
et un sous-shell:Cela n'utilise pas
sed
ouawk
mais il n'utilise pas noncut
plus, donc je ne suis pas sûr si cela peut être considéré comme une réponse à la question telle qu'elle est formulée.Cela ne fonctionne pas bien si vous traitez des chaînes d'entrée pouvant contenir des barres obliques. Une solution de contournement pour cette situation serait de remplacer la barre oblique par un autre caractère dont vous savez qu'il ne fait pas partie d'une chaîne d'entrée valide. Par exemple, le caractère pipe (
|
) n'est pas non plus autorisé dans les noms de fichiers, donc cela fonctionnerait:la source
ce qui suit met en œuvre la suggestion d'un ami
la source
echo
pour que cela fonctionne de manière fiable et robuste. Voir stackoverflow.com/questions/10067266/…Si vous avez un fichier nommé filelist.txt qui est une liste de chemins tels que les suivants: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h
alors vous pouvez le faire: rev filelist.txt | cut -d "/" -f1 | tour
la source
Ajout d'une approche à cette vieille question juste pour le plaisir:
Outre bash, seule la coupe est utilisée. Eh bien, et l'écho, je suppose.
la source
while read -r line; do echo ${line/*;}; done <input.file
donne le même résultat.J'ai réalisé que si nous nous contentions de nous assurer qu'un délimiteur de fin existe, cela fonctionne. Donc, dans mon cas, j'ai des délimiteurs de virgule et d'espaces. J'ajoute un espace à la fin;
la source
ans="a, b, c"
produitb
, qui ne répond pas aux exigences de "nombre de champs sont inconnus ou changent à chaque ligne" .