242 votes

Rechercher et remplacer dans bash en utilisant des expressions régulières

J'ai déjà vu cet exemple :

hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//[0-9]/}

Ce qui suit cette syntaxe : ${variable//pattern/replacement}

Malheureusement, le pattern ne semble pas supporter la syntaxe regex complète (si j'utilise le champ . o \s par exemple, il essaie de faire correspondre les caractères littéraux).

Comment rechercher/remplacer une chaîne de caractères en utilisant la syntaxe regex complète ?

0 votes

J'ai trouvé une question connexe ici : stackoverflow.com/questions/5658085/

3 votes

POUR INFORMATION, \s ne fait pas partie de la syntaxe standard des expressions régulières définies par POSIX (ni BRE ni ERE) ; il s'agit d'une extension de PCRE, qui n'est généralement pas disponible dans l'interpréteur de commandes. [[:space:]] est l'équivalent le plus universel.

2 votes

\s peut être remplacé par [[:space:]] En passant, je vous signale qu'il n'y a pas d'autre solution que de faire appel à l'aide, . por ? et extglob, des extensions au langage de base du shell peuvent être utilisées pour des choses telles que des sous-groupes optionnels, des groupes répétés, etc.

6voto

Dabe Murphy Points 81

Je sais que c'est un ancien fil de discussion, mais c'était mon premier résultat sur Google, et je voulais partager ce qui suit resub que j'ai mis en place, qui ajoute le support pour les références multiples de 1$, 2$, etc...

#!/usr/bin/env bash

############################################
###  resub - regex substitution in bash  ###
############################################

resub() {
    local match="$1" subst="$2" tmp

    if [[ -z $match ]]; then
        echo "Usage: echo \"some text\" | resub '(.*) (.*)' '\$2 me \${1}time'" >&2
        return 1
    fi

    ### First, convert "$1" to "$BASH_REMATCH[1]" and 'single-quote' for later eval-ing...

    ### Utility function to 'single-quote' a list of strings
    squot() { local a=(); for i in "$@"; do a+=( $(echo \'${i//\'/\'\"\'\"\'}\' )); done; echo "${a[@]}"; }

    tmp=""
    while [[ $subst =~ (.*)\${([0-9]+)}(.*) ]] || [[ $subst =~ (.*)\$([0-9]+)(.*) ]]; do
        tmp="\${BASH_REMATCH[${BASH_REMATCH[2]}]}$(squot "${BASH_REMATCH[3]}")${tmp}"
        subst="${BASH_REMATCH[1]}"
    done
    subst="$(squot "${subst}")${tmp}"

    ### Now start (globally) substituting

    tmp=""
    while read line; do
        counter=0
        while [[ $line =~ $match(.*) ]]; do
            eval tmp='"${tmp}${line%${BASH_REMATCH[0]}}"'"${subst}"
            line="${BASH_REMATCH[$(( ${#BASH_REMATCH[@]} - 1 ))]}"
        done
        echo "${tmp}${line}"
    done
}

resub "$@"

##################
###  EXAMPLES  ###
##################

###  % echo "The quick brown fox jumps quickly over the lazy dog" | resub quick slow
###    The slow brown fox jumps slowly over the lazy dog

###  % echo "The quick brown fox jumps quickly over the lazy dog" | resub 'quick ([^ ]+) fox' 'slow $1 sheep'
###    The slow brown sheep jumps quickly over the lazy dog

###  % animal="sheep"
###  % echo "The quick brown fox 'jumps' quickly over the \"lazy\" \$dog" | resub 'quick ([^ ]+) fox' "\"\$low\" \${1} '$animal'"
###    The "$low" brown 'sheep' 'jumps' quickly over the "lazy" $dog

###  % echo "one two three four five" | resub "one ([^ ]+) three ([^ ]+) five" 'one $2 three $1 five'
###    one four three two five

###  % echo "one two one four five" | resub "one ([^ ]+) " 'XXX $1 '
###    XXX two XXX four five

###  % echo "one two three four five one six three seven eight" | resub "one ([^ ]+) three ([^ ]+) " 'XXX $1 YYY $2 '
###    XXX two YYY four five XXX six YYY seven eight

H/T à Charles Duffy re : (.*)$match(.*)

1voto

miles bennet Points 575

Définir la var

hello=ho02123ware38384you443d34o3434ingtod38384day

puis, echo avec remplacement regex sur var

echo ${hello//[[:digit:]]/}

et cela s'imprimera :

howareyoudoingtodday

Extra - si vous souhaitez le contraire (pour obtenir les caractères numériques)

echo ${hello//[![:digit:]]/}

et ceci s'imprimera :

021233838444334343438384

0 votes

C'est à peu près le même code que celui de la question. Il vous manque la partie concernant la façon dont "le pattern ne semble pas prendre en charge la syntaxe complète des expressions rationnelles (si j'utilise le champ . o \s par exemple, il essaie de faire correspondre les caractères littéraux)". - Vous ne pouvez pas faire echo ${hello//[[:digit:]\s]/} par exemple.

0 votes

@AdamKatz Oui, ce n'est pas grave, ça arrive. Merci

0voto

Tono Nam Points 4465

Cet exemple dans l'entrée hello ugly world il recherche le regex bad|ugly et le remplace par nice

#!/bin/bash

# THIS FUNCTION NEEDS THREE PARAMETERS
# arg1 = input              Example:  hello ugly world
# arg2 = search regex       Example:  bad|ugly
# arg3 = replace            Example:  nice
function regex_replace()
{
  # $1 = hello ugly world
  # $2 = bad|ugly
  # $3 = nice

  # REGEX
  re="(.*?)($2)(.*)"

  if [[ $1 =~ $re ]]; then
    # if there is a match

    # ${BASH_REMATCH[0]} = hello ugly world
    # ${BASH_REMATCH[1]} = hello 
    # ${BASH_REMATCH[2]} = ugly
    # ${BASH_REMATCH[3]} = world    

    # hello + nice + world
    echo ${BASH_REMATCH[1]}$3${BASH_REMATCH[3]}
  else    
    # if no match return original input  hello ugly world
    echo "$1"
  fi    
}

# prints 'hello nice world'
regex_replace 'hello ugly world' 'bad|ugly' 'nice'

# to save output to a variable
x=$(regex_replace 'hello ugly world' 'bad|ugly' 'nice')
echo "output of replacement is: $x"
exit

-4voto

Markus Dutschke Points 1214

Vous pouvez utiliser Python. Ce n'est pas très efficace, mais cela permet de faire le travail avec une syntaxe un peu plus souple.

appliquer au dossier

Le script python suivant remplacera "FROM" (mais pas "notFrom") par "TO".

regex_replace.py

import sys
import re

for line in sys.stdin:
    line = re.sub(r'(?<!not)FROM', 'TO', line)
    sys.stdout.write(line)

Vous pouvez appliquer cela sur un fichier texte, comme

$ cat test.txt
bla notFROM
FROM FROM
bla bla
FROM bla

bla  notFROM FROM

bla FROM
bla bla

$ cat test.txt | python regex_replace.py
bla notFROM
TO TO
bla bla
TO bla

bla  notFROM TO

bla TO
bla bla

appliquer sur la variable

#!/bin/bash

hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello

PYTHON_CODE=$(cat <<END
import sys
import re

for line in sys.stdin:
    line = re.sub(r'[0-9]', '', line)
    sys.stdout.write(line)
END
)
echo $hello | python -c "$PYTHON_CODE"

sortie

ho02123ware38384you443d34o3434ingtod38384day
howareyoudoingtodday

0 votes

Je le downvote car j'ai cherché "utiliser les expressions régulières dans Bash". Python ne m'aidera pas à paramétrer mon prompt PS1 (afaik).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X