J'ai ce petit script dans sh
(Mac OSX 10.6) pour parcourir un tableau de fichiers. Google a cessé d'être utile à ce stade:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
Jusqu'à présent (évidemment, pour vous, gourous du shell), il $name
ne contient que 0, 1 ou 2, selon grep
que le nom de fichier correspond au sujet fourni. Ce que j'aimerais, c'est capturer ce qui est à l'intérieur des parens ([a-z]+)
et le stocker dans une variable .
J'aimerais utiliser grep
uniquement, si possible . Sinon, s'il vous plaît pas de Python ou Perl, etc. sed
ou quelque chose comme ça - je suis nouveau dans le shell et je voudrais attaquer cela sous l'angle puriste * nix.
De plus, en tant que bonu super cool , je suis curieux de savoir comment je peux concaténer une chaîne en shell? Est-ce que le groupe que j'ai capturé était la chaîne "un nom" stockée dans $ name, et je voulais ajouter la chaîne ".jpg" à la fin, est-ce possible cat $name '.jpg'
?
Veuillez expliquer ce qui se passe, si vous avez le temps.
grep
, alors cesed
serait génial, s'il est possible de résoudre en utilisantsed
.Réponses:
Si vous utilisez Bash, vous n'avez même pas besoin d'utiliser
grep
:Il est préférable de mettre l'expression régulière dans une variable. Certains modèles ne fonctionneront pas s'ils sont inclus littéralement.
Ceci utilise
=~
qui est l'opérateur de correspondance d'expressions régulières de Bash. Les résultats de la correspondance sont enregistrés dans un tableau appelé$BASH_REMATCH
. Le premier groupe de capture est stocké dans l'index 1, le second (le cas échéant) dans l'index 2, etc. L'index zéro est la correspondance complète.Vous devez savoir que sans ancrages, cette expression régulière (et celle qui utilise
grep
) correspondra à l'un des exemples suivants et plus, ce qui peut ne pas être ce que vous recherchez:Pour éliminer les deuxième et quatrième exemples, faites votre regex comme ceci:
qui dit que la chaîne doit commencer par un ou plusieurs chiffres. Le carat représente le début de la chaîne. Si vous ajoutez un signe dollar à la fin de l'expression régulière, comme ceci:
alors le troisième exemple sera également éliminé car le point ne fait pas partie des caractères de l'expression régulière et le signe dollar représente la fin de la chaîne. Notez que le quatrième exemple échoue également cette correspondance.
Si vous avez GNU
grep
(environ 2,5 ou plus tard, je pense, lorsque l'\K
opérateur a été ajouté):L'
\K
opérateur (recherche de longueur variable) entraîne la correspondance du modèle précédent, mais n'inclut pas la correspondance dans le résultat. L'équivalent de longueur fixe est(?<=)
- le motif serait inclus avant la parenthèse fermante. Vous devez utiliser\K
si quantificateurs peuvent correspondre à des chaînes de longueurs différentes (par exemple+
,*
,{2,4}
).L'
(?=)
opérateur correspond à des modèles de longueur fixe ou variable et est appelé "anticipation". Il n'inclut pas non plus la chaîne correspondante dans le résultat.Afin de rendre la correspondance insensible à la casse, l'
(?i)
opérateur est utilisé. Il affecte les motifs qui le suivent, sa position est donc significative.Le regex peut avoir besoin d'être ajusté selon qu'il y a d'autres caractères dans le nom de fichier. Vous remarquerez que dans ce cas, je montre un exemple de concaténation d'une chaîne en même temps que la sous-chaîne est capturée.
la source
/K
opérateur roches.grep
. Il a également été accepté par le PO et a beaucoup voté. Merci pour le downvote.Ce n'est pas vraiment possible avec pure
grep
, du moins pas en général.Mais si votre modèle convient, vous pouvez utiliser
grep
plusieurs fois dans un pipeline pour réduire d'abord votre ligne à un format connu, puis extraire juste le bit que vous voulez. (Bien que les outils aimentcut
etsed
sont bien meilleurs dans ce domaine).Supposons à titre d'argument que votre modèle était un peu plus simple:
[0-9]+_([a-z]+)_
vous pouvez extraire ceci comme ceci:Le premier
grep
supprimerait toutes les lignes qui ne correspondraient pas à votre motif global, le secondgrep
(qui l'a--only-matching
spécifié) afficherait la partie alpha du nom. Cela ne fonctionne que parce que le motif est approprié: la "partie alpha" est suffisamment spécifique pour extraire ce que vous voulez.(À part: Personnellement, j'utiliserais
grep
+cut
pour obtenir ce que vous recherchez:echo $name | grep {pattern} | cut -d _ -f 2
cela permetcut
d'analyser la ligne en champs en la divisant sur le délimiteur_
et renvoie uniquement le champ 2 (les numéros de champ commencent à 1)).La philosophie Unix est d'avoir des outils qui font une chose, et le font bien, et les combinent pour réaliser des tâches non triviales, donc je dirais que
grep
+sed
etc est une façon plus Unixy de faire les choses :-)la source
for f in $files; do name=
écho $ f | grep -oEi '[0-9] + _ ([az] +) _ [0-9a-z] *' | couper -d _ -f 2;
Aha!Je me rends compte qu'une réponse a déjà été acceptée pour cela, mais sous un "angle puriste strictement * nix", il semble que le bon outil pour le travail soit
pcregrep
, ce qui ne semble pas encore avoir été mentionné. Essayez de changer les lignes:aux éléments suivants:
pour obtenir uniquement le contenu du groupe de capture 1.
L'
pcregrep
outil utilise la même syntaxe que celle que vous avez déjà utiliséegrep
, mais implémente les fonctionnalités dont vous avez besoin.Le paramètre
-o
fonctionne comme lagrep
version s'il est nu, mais il accepte également un paramètre numérique danspcregrep
, qui indique le groupe de capture que vous souhaitez afficher.Avec cette solution, le script requiert un minimum de modifications. Vous remplacez simplement un utilitaire modulaire par un autre et ajustez les paramètres.
Remarque intéressante: vous pouvez utiliser plusieurs arguments -o pour renvoyer plusieurs groupes de capture dans l'ordre dans lequel ils apparaissent sur la ligne.
la source
pcregrep
n'est pas disponible par défaut dansMac OS X
lequel l'OP utilisepcregrep
ne semble pas comprendre le chiffre après la-o
: "Lettre d'option inconnue '1' dans" -o1 ". Aucune mention de cette fonctionnalité en regardantpcregrep --help
7.8 2008-09-05
.echo 'r123456 foo 2016-03-17' | pcregrep -o1 'r([0-9]+)' 123456
pcregrep
8,41 (installé avecapt-get install pcregrep
leUbuntu 16.03
) ne reconnaît pas le-Ei
commutateur. Mais cela fonctionne parfaitement sans. Sur macOS, avecpcregrep
installé viahomebrew
(également 8.41) comme le mentionne @anishpatel ci-dessus, au moins sur High Sierra, le-E
commutateur n'est pas non plus reconnu.Pas possible en juste grep je crois
pour sed:
Je vais essayer le bonus:
la source
sed
solution ne fonctionne pas. Il imprime simplement tout dans mon répertoire.Il s'agit d'une solution qui utilise gawk. C'est quelque chose que je trouve que je dois utiliser souvent, donc j'ai créé une fonction pour cela
utiliser juste faire
la source
\s
. Savez-vous comment y remédier?Une suggestion pour vous - vous pouvez utiliser l'expansion des paramètres pour supprimer la partie du nom du dernier trait de soulignement, et de la même manière au début:
Alors
name
aura la valeurabc
.Consultez la documentation des développeurs Apple , recherchez «Extension des paramètres».
la source
si vous avez bash, vous pouvez utiliser la globalisation étendue
ou
la source