[regex] 정규식을 사용하여 bash에서 검색 및 바꾸기

이 예를 보았습니다.

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

다음 구문을 따릅니다. ${variable//pattern/replacement}

불행히도 pattern필드는 전체 정규식 구문을 지원하지 않는 것 같습니다 ( 예를 들어 .또는을 사용하는 경우 \s리터럴 문자와 일치하려고 시도합니다).

전체 정규식 구문을 사용하여 문자열을 검색 / 바꾸려면 어떻게해야합니까?



답변

sed 사용 :

MYVAR=ho02123ware38384you443d34o3434ingtod38384day
echo "$MYVAR" | sed -e 's/[a-zA-Z]/X/g' -e 's/[0-9]/N/g'
# prints XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

후속 작업 -e은 순서대로 처리됩니다. 또한 g표현식 의 플래그는 입력의 모든 항목과 일치합니다.

이 방법을 사용하여 좋아하는 도구를 선택할 수도 있습니다 (예 : perl, awk).

echo "$MYVAR" | perl -pe 's/[a-zA-Z]/X/g and s/[0-9]/N/g'

이렇게하면 더 창의적인 일치를 수행 할 수 있습니다 … 예를 들어 위의 캡처에서 첫 번째 표현식에 일치하는 항목이 없으면 숫자 대체가 사용되지 않습니다 (지연 and평가 로 인해 ). 그리고 물론, 당신은 당신의 입찰을 위해 Perl의 완전한 언어 지원을 가지고 있습니다.


답변

이것은 실제로 순수한 bash에서 수행 할 수 있습니다.

hello=ho02123ware38384you443d34o3434ingtod38384day
re='(.*)[0-9]+(.*)'
while [[ $hello =~ $re ]]; do
  hello=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
done
echo "$hello"

… 수율 …

howareyoudoingtodday


답변

이 예제는 sed를 사용할 필요없이 bash에서도 작동합니다.

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[a-zA-Z]/X}
echo ${MYVAR//[0-9]/N}

문자 클래스 대괄호 표현식을 사용할 수도 있습니다.

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[[:alpha:]]/X}
echo ${MYVAR//[[:digit:]]/N}

산출

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

그러나 @Lanaru가 알고 싶었던 것은 내가 질문을 올바르게 이해했다면 “전체”또는 PCRE 확장이 왜 필요한지입니다. \s\S\w\W\d\D 등이 php ruby ​​python 등에서 지원 작동하지 않는 . 이러한 확장은 Perl 호환 정규식 (PCRE)에서 가져온 것입니다. 다른 형태의 쉘 기반 정규식과 호환되지 않을 수 있습니다.

작동하지 않습니다.

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//\d/}


#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | sed 's/\d//g'

모든 리터럴 “d”문자가 제거 된 출력

ho02123ware38384you44334o3434ingto38384ay

하지만 다음은 예상대로 작동합니다.

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | perl -pe 's/\d//g'

산출

howareyoudoingtodday

좀 더 명확 해지기를 바라지 만 아직 혼란스럽지 않다면 REG_ENHANCED 플래그가 활성화 된 Mac OS X에서이 작업을 시도해보십시오.

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day;
echo $MYVAR | grep -o -E '\d'

대부분의 * nix 버전에서는 다음 출력 만 표시됩니다.

d
d
d

nJoy!


답변

반복적 인 호출을하고 성능에 관심이있는 경우이 테스트는 BASH 메서드가 sed 및 다른 외부 프로세스보다 15 배 더 빠르다는 것을 보여줍니다.

hello=123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X

P1=$(date +%s)

for i in {1..10000}
do
   echo $hello | sed s/X//g > /dev/null
done

P2=$(date +%s)
echo $[$P2-$P1]

for i in {1..10000}
do
   echo ${hello//X/} > /dev/null
done

P3=$(date +%s)
echo $[$P3-$P2]


답변

[[:digit:]]패턴으로 사용 (이중 대괄호 참고) :

$ hello=ho02123ware38384you443d34o3434ingtod38384day
$ echo ${hello//[[:digit:]]/}
howareyoudoingtodday

답변을 요약하고 싶었습니다 (특히 @ nickl-의 https://stackoverflow.com/a/22261334/2916086 ).


답변

나는 이것이 고대의 스레드라는 것을 알고 있지만, 그것은 Google에서 처음으로 히트 한 것이었고 resub, 여러 $ 1, $ 2 등에 대한 지원을 추가 하는 다음 을 함께 공유하고 싶었습니다 .

#!/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

@Charles Duffy 에게 H / T 다시 :(.*)$match(.*)


답변