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

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%% *}"

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

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%% *}

2

Par exemple, si vous utilisez GNU grep:

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

Il utilise PCRE.


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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.