Rechercher une chaîne tout en en connaissant une partie et renvoyer une chaîne

9

J'ai une chaîne, par exemple

"Icecream123 AirplaneBCD CompanyTL1 ComputerYU1"

Disons que je sais que ma chaîne contiendra à coup sûr la sous-chaîne IceCream mais je ne sais pas ce qui la suit.

Ce pourrait être 123 comme dans mon exemple ou ce pourrait être quelque chose de différent.

Bien que je puisse utiliser grep pour détecter si la sous-chaîne "Icecream" existe dans ma chaîne avec la commande suivante

echo $string | grep -oF 'Icecream';

Qui va imprimer

Icecream

Je veux avec une commande pour l'obtenir pour imprimer la sous-chaîne entière, qui dans mon exemple est

Icecream123

Bien sûr, ce qui suit Icecream est aléatoire et n'est pas connu à l'avance, donc je ne peux pas simplement faire

$SUBSTRING=$(echo $string | grep -oF 'Icecream')
$SUBSTRINGTRAIL=123
echo $SUBSTRING$SUBSTRINGTRAIL
Sonamor
la source
la sous-chaîne est-elle fixe / statique - toujours "Icecream", ou est-elle variable?
Jeff Schaller
un espace indiquera-t-il la fin du suffixe souhaité?
Jeff Schaller
@JeffSchaller Malheureusement, je ne le sais pas. Je reçois en fait une sortie multiligne d'une autre commande, que je stocke dans une variable, cette variable est ma chaîne $, quand elle est en écho, elle affiche la sortie multiligne comme une ligne de signal avec un espace entre elles. Je ne sais pas vraiment si c'est un espace ou un caractère spécial comme LF. Je pensais que c'était de l'espace.
Sonamor
Je veux dire, par exemple, Icecream123 AirplaneBCDvous voulez vous arrêter à 123. Est-ce parce qu'il y a un espace après le 3, ou autre chose?
Jeff Schaller
1
Si vous n'êtes pas sûr de vos données, il est difficile d'écrire une solution appropriée. Jusqu'à présent, toutes les réponses supposent que vos données sont sur une seule ligne, comme vous l'avez montré. J'essayais de comprendre quel était votre délimiteur - où la partie "arrière" devrait s'arrêter.
Jeff Schaller

Réponses:

15

Si votre grepprend en charge les expressions régulières compatibles Perl, vous pouvez faire correspondre sans avidité jusqu'à la limite de mot suivante:

echo "$string" | grep -oP 'Icecream.*?\b'

Sinon, faites correspondre la plus longue séquence de caractères non vides:

echo "$string" | grep -o 'Icecream[^[:blank:]]*'

Ou gardez tout dans le shell et supprimez la séquence de caractères la plus longue commençant par un espace:

echo "${string%% *}"
tournevis
la source
2
Pour le PCRE, j'utiliserais 'Icecream\S+'pour certains caractères non vides.
glenn jackman
Merci pour vos commentaires, il semble que ma version de grep ne supporte pas perl regex. Pourriez-vous ajouter plus de détails sur votre troisième option? Je ne sais pas trop comment le mettre en œuvre.
Sonamor
Après quelques tests supplémentaires, il semble que l'utilisation de l'écho "$ string" | grep -oP 'Icecream. *? \ b' ou 'Icecream \ S +' il fait le travail. Merci
Sonamor
c'est vraiment déroutant que bien que votre variable $ string soit une chaîne, vous devez toujours la mettre entre guillemets!
Sonamor
@Sonamor dans ce cas, le devis n'est pas strictement nécessaire; mais il y a tellement de cas où il est que c'est une bonne habitude à prendre. Voir par exemple Quand un guillemet double est-il nécessaire?
steeldriver
7

Utiliser un grepqui connaît -o:

$ printf '%s\n' "$string" | grep -o '\<Icecream[^[:blank:]]*'
Icecream123

Le modèle \<Icecream[^[:blank:]]*correspond à la chaîne Icecream(où le Iest précédé d'un caractère autre qu'un mot ou au début de la ligne) suivi de zéro ou plusieurs espaces non (pas d'espaces ni de tabulations).


En utilisant awk:

$ printf '%s\n' "$string" | awk -v RS=' ' '/^Icecream/'       
Icecream123

Le awkprogramme divise la chaîne en enregistrements séparés par des espaces et teste chacun. Il imprimera ceux qui commencent par la chaîne Icecream.

En utilisant mawkou GNU awk, vous pouvez également utiliser

printf '%s\n' "$string" | awk -v RS='[[:blank:]]' '/^Icecream/'

puisqu'ils interpet RScomme expression régulière s'il contient plus d'un caractère.


Avec sed, de la même manière qu'avec grep:

$ printf '%s\n' "$string" | sed 's/.*\(\<Icecream[^[:blank:]]*\).*/\1/'
Icecream123

En utilisant /bin/sh:

set -- Icecream123 AirplaneBCD CompanyTL1 ComputerYU1
for string; do
    case $string in
        Icecream*)
            printf '%s\n' "$string"
            break
    esac
done

Perl (avec un peu d'aide de tr):

$ printf '%s\n' "$string" | tr ' ' '\n' | perl -ne '/Icecream\S*/ && print'
Icecream123

ou juste

$ printf '%s\n' "$string" | perl -ne '/(Icecream\S*)/ && print $1, "\n"'
Icecream123
Kusalananda
la source
Ou, divisez-vous en lignes et faites correspondre la clé:echo "$string" | grep -o '\S\+' | grep "Icecream"
Isaac
7

Depuis que vous avez marqué bash:

[[ $string =~ (Icecream[^ ]*) ]] && result=${BASH_REMATCH[1]}

Plus généralement, pour un terme de recherche dans $search:

[[ $string =~ ($search[^ ]*) ]] && result=${BASH_REMATCH[1]}

... ou avec extension des paramètres:

# remove any leading text up to -and through- the search text:
x=${string##*$search}

# remove any trailing space onwards
result=$search${x%% *}
Jeff Schaller
la source
2

Par exemple, si vous utilisez GNU grep:

$ echo "Icecream123 AirplaneBCD CompanyTL1 ComputerYU1" | grep -oP '\bIcecream.*?(\s|$)' --color

Il utilise PCRE.

Arkadiusz Drabczyk
la source
1

Un peu plus simple peut-être, d'autant plus que vous dites que votre version de grep ne supporte pas perge regex:

$ echo $string | tr ' ' '\n' | grep 'Icecream' Icecream123

Le trdivise la chaîne en lignes en remplaçant tous les espaces par des retours à la ligne. Ensuite, vous pouvez utiliser grepfacilement.

Vous pouvez également écrire ce qui suit pour obtenir uniquement ce qui suit le mot que vous recherchez:

$ echo $string | tr ' ' '\n' | sed -n 's/Icecream//p' 123

Loi29
la source