Comment trouver la position d'un personnage en utilisant grep?

10

J'ai besoin d'identifier la position d'un caractère dans la chaîne à l'aide de la commande grep.

Exemple, la chaîne est RAMSITALSKHMAN|1223333.

grep -n '[^a-zA-Z0-9\$\~\%\#\^]'

Comment trouver la position de |dans la chaîne donnée?

user82782
la source
ça doit être avec grep?
Braiam

Réponses:

28

Vous pouvez utiliser -bpour obtenir le décalage d'octet, qui est le même que la position pour le texte simple (mais pas pour UTF-8 ou similaire).

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|'
14:|

Dans ce qui précède, j'utilise le -acommutateur pour dire à grep d'utiliser l'entrée comme texte; nécessaire lorsque vous travaillez sur des fichiers binaires, et le -ocommutateur pour sortir uniquement les caractères correspondants.

Si vous ne voulez que la position, vous pouvez utiliser grep pour extraire uniquement la position:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' | grep -oE '[0-9]+'
14

Si vous obtenez une sortie bizarre, vérifiez si grep a activé les couleurs. Vous pouvez désactiver les couleurs en passant --colors=neverà grep, ou en préfixant la commande grep avec un \(qui désactivera tous les alias), par exemple:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' --color=never | \grep -oE '^[0-9]+'
14

Pour une chaîne qui renvoie plusieurs correspondances, dirigez-la head -n1pour obtenir la première correspondance.

Notez que j'utilise les deux dans ce qui précède, et notez que ce dernier ne fonctionnera pas si grep est "aliasé" via un exécutable (script ou autre), uniquement lors de l'utilisation d'alias.

runejuhl
la source
3
Recherchez maintenant 2;)
Izkata
Merci @Izkata, vous avez raison. J'ai un peu mis à jour mon message et ajouté le chapeau manquant ^:)
runejuhl
1
Quelle version de grep avez-vous utilisée? Je reçois 0:|comme output-- parce que 0 est la position d'octet de début de la ligne où |se trouve.
Alex
@ Alex GNU grep d'étirement Debian: grep (GNU grep) 2.27. Utilisez-vous peut-être OS X?
runejuhl
11

Essayer:

printf '%s\n' 'RAMSITALSKHMAN|1223333.' | grep -o . | grep -n '|'

production:

15:|

Cela vous donnera la position avec l'indice basé sur 1.

cuonglm
la source
Cela
1
@ user82782: Quelle commande avez-vous exécutée? Comment tu sais que ça n'a pas marché?
cuonglm du
printf '%s\n' '|' | grep -o . | grep -n '|'impressions 1, pas 0comme prévu.
l0b0
1
@ l0b0: L'OP ne dit pas qu'il voulait une base d'index 0 ou 1.
cuonglm
Je veux juste dire ce à quoi un développeur de logiciels pourrait s'attendre.
l0b0
8

Si vous utilisez le shell , vous pouvez utiliser des opérations purement intégrées sans avoir besoin de générer des processus externes tels que ou :

$ str="RAMSITALSKHMAN|1223333"
$ tmp="${str%%|*}"
$ if [ "$tmp" != "$str" ]; then
> echo ${#tmp}
> fi
14
$ 

Cela utilise une extension de paramètre pour supprimer toutes les occurrences de |suivis par n'importe quelle chaîne et l'enregistrer dans une variable temporaire. Il suffit alors de mesurer la longueur de la variable temporaire pour obtenir l'indice de |.

Notez que ifvérifie si le |existe dans la chaîne d'origine. Si ce n'est pas le cas, la variable temporaire sera la même que l'original.

Notez également que cela fournit l'index de base zéro |qui est généralement utile lors de l'indexation de chaînes bash. Cependant, si vous avez besoin de l'index à base unique, vous pouvez le faire:

$ echo $((${#tmp}+1))
15
$ 
Traumatisme numérique
la source
1
probablement la meilleure réponse, cette syntaxe est belle et si rapide et facile à utiliser lorsque vous comprenez sa signification, vive le cœur
vdegenne
4

Vous pouvez utiliser la indexfonction awk pour renvoyer la position en caractères où la correspondance se produit:

echo "RAMSITALSKHMAN|1223333"|awk 'END{print index($0,"|")}'
15

Si cela ne vous dérange pas d'utiliser la indexfonction Perl , cela gère le signalement de zéro, une ou plusieurs occurrences d'un caractère:

echo "|abc|xyz|123456|zzz|" | \
perl -nle '$pos=-1;while (($off=index($_,"|",$pos))>=0) {print $off;$pos=$off+1}'

Pour des raisons de lisibilité uniquement, le pipeline a été divisé en deux lignes.

Tant que le caractère cible est trouvé, indexrenvoie une valeur positive basée sur zéro (0). Par conséquent, la chaîne "abc | xyz | 123456 | zzz |" lorsque analysé renvoie les positions 0, 4, 8, 15 et 19.

JRFerguson
la source
pour cette utilisation, awk est plus utile / facile que grep.
Archemar
Cela n'imprimera que la première position, ne fonctionnera pas avec une chaîne commeRAMSITALSKHMAN|1|223333
cuonglm
3

Nous pouvons aussi le faire en utilisant "expr match" ou "expr index"

expr correspond à $ string $ substring où $ substring est un RE.

echo `expr match "RAMSITALSKHMAN|1223333" '[A-Z]*.|'`

Et ci-dessus vous donnera la position car elle renvoie la longueur de la sous-chaîne correspondante.

Mais pour être plus précis dans la recherche d'index:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`
bluefoggy
la source
Je n'ai pas assez de réputation pour commenter ailleurs. J'ai personnellement aimé la réponse donnée par @Gnouc. Cependant, pourquoi utiliser awk et le rendre complexe quand nous pouvons faire des choses simples en utilisant 'expr'
bluefoggy
@kingsdeb c'est juste une suggestion.
Avinash Raj
@kingsdeb: Parce que (1) les awksolutions peuvent être modifiées de manière triviale pour rapporter ces informations sur chaque ligne d'un fichier (tout ce que vous avez à faire est de supprimer le END, qui n'a jamais été vraiment nécessaire, de la réponse de JRFerguson, et Avinash Raj le fait déjà) ; alors que pour faire cela avec la exprsolution, vous auriez besoin d'ajouter une boucle explicite (et la réponse de Gnouc n'est pas facilement adaptable pour le faire, du moins je le vois), et (2) les awksolutions peuvent être adaptées pour rapporter tous les correspond à chaque ligne un peu plus facilement que la exprsolution (en fait, Avinash Raj fait déjà cela aussi).
G-Man dit `` Réintègre Monica '' le
Pourquoi utiliseriez-vous echo `...`ici?
Stéphane Chazelas
C'est juste pour montrer la sortie ici
bluefoggy
2

Une autre commande awk ,

$ echo 'RAMSITALSKHMAN|1223333'| awk 'BEGIN{ FS = "" }{for(i=1;i<=NF;i++){if($i=="|"){print i;}}}'
15

En définissant le séparateur de champs comme chaîne nulle, awk transforme les caractères individuels de l'enregistrement en champs séparés.

Avinash Raj
la source
2

quelques alternatives incluent:

similaire à la réponse de Gnouc, mais avec la coquille:

echo 'RAMSITALSKHMAN|1223333' |
tr -c \| \\n | 
sh

sh: line 15: syntax error near unexpected token `|
sh: line 15: `|'

avec sedet dcéventuellement couvrant plusieurs lignes:

echo 'RAMSITALSKHMAN|1223333' |
sed 's/[^|]/1+/g;s/|/p/;1i0 1+' |dc

15

avec $IFS...

IFS=\|; set -f; set -- ${0+RAMSITALSKHMAN|1223333}; echo $((${#1}+1))

Cela aussi vous dire combien beaucoup il y a comme ...

echo $(($#-1))
mikeserv
la source