ripgrep으로 트릭 검색 및 바꾸기

ripgrep (명령 이름 rg )은 grep 도구이지만 검색 및 바꾸기도 지원합니다. rgsed 에 대한 유사 대안과는 거리가 멀지만, 여러 줄 교체, 고정 문자열 일치, PCRE2 지원 등과 같은 멋진 기능이 있습니다. 이 게시물은 대체 구문에 대한 개요를 제공하고 일부를 강조 표시합니다. rgsed 를 쉽게 대체하는 경우.

전역 검색 및 바꾸기




$ cat ip.txt
dark blue, light blue
light orange
blue sky

# by default, line number is displayed if output destination is stdout
# by default, only lines that matched the given pattern is displayed
# 'blue' is search pattern and -r 'red' is replacement string
$ rg 'blue' -r 'red' ip.txt
1:dark red, light red
3:red sky

# --passthru option is useful to print all lines, whether or not it matched
# -N will disable line number prefix
# this command is similar to: sed 's/blue/red/g' ip.txt
$ rg --passthru -N 'blue' -r 'red' ip.txt
dark red, light red
light orange
red sky


N번째 발생 일치



이전 예에서 볼 수 있듯이 rg는 모든 항목을 검색하고 바꿉니다. 따라서 입력 줄당 특정 항목만 교체하려면 regexp를 사용하여 창의적이어야 합니다.

$ s='see bat hot at but at go gate at sat at but at'

# replace first occurrence only
# same as: sed 's/\bat\b/[xyz]/'
$ echo "$s" | rg --passthru -N '\bat\b(.*)' -r '[xyz]$1'
see bat hot [xyz] but at go gate at sat at but at

# same as: sed 's/\bat\b/[xyz]/3'
# the number within {} is N-1 to replace Nth occurrence, for N>1
$ echo "$s" | rg --passthru -N '^((.*?\bat\b){2}.*?)\bat\b' -r '$1[xyz]'
see bat hot at but at go gate [xyz] sat at but at

# replace last but Nth occurrence, for N>=0
$ echo "$s" | rg --passthru -N '^(.*)\bat\b((.*\bat\b){3})' -r '$1[xyz]$2'
see bat hot at but [xyz] go gate at sat at but at


적절한 해결 방법


rg는 in-place 옵션을 지원하지 않으므로 직접 수행해야 합니다.

# -N isn't needed here as output destination is a file
# same as: sed -i 's/blue/red/g' ip.txt
$ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt

$ cat ip.txt
dark red, light red
light orange
red sky


moreutils installed 이 있는 경우 sponge 도 사용할 수 있습니다.

rg --passthru 'blue' -r 'red' ip.txt | sponge ip.txt


녹 정규식 및 PCRE2



기본적으로 rgGNU sed 에 비해 훨씬 더 많은 기능을 갖춘 Rust 정규식을 사용합니다. 지원되지 않는 주요 기능은 regexp 정의 내의 역참조입니다(성능상의 이유로). 정규식 구문 및 기능은 Rust regex documentation을 참조하십시오. rg는 기본적으로 유니코드를 지원합니다.

# non-greedy quantifier is supported
$ s='food land bark sand band cue combat'
$ echo "$s" | rg --passthru 'foo.*?ba' -r '[xyz]'
[xyz]rk sand band cue combat

# unicode support
$ echo 'fox:αλεπού,eagle:αετός' | rg --passthru '\p{L}+' -r '($0)'
(fox):(αλεπού),(eagle):(αετός)

# set operator example, remove all punctuation characters except . ! and ?
$ para='"hi", there! how *are* you? all fine here.'
$ echo "$para" | rg --passthru '[[:punct:]--[.!?]]+' -r ''
hi there! how are you? all fine here.

-P 스위치는 더 많은 트릭이 있는 PCRE2 플레이버를 활성화합니다. --engine=auto를 사용하여 필요할 때 rg가 자동으로 PCRE2를 사용하도록 할 수도 있습니다(예: rg 명령의 별칭으로 유용하여 기본적으로 Rust 엔진의 성능을 제공하고 필요).

# backreference within regexp definition
$ s='cocoa appleseed tool speechless'
$ echo "$s" | rg --passthru -wP '([a-z]*([a-z])\2[a-z]*){2}' -r '{$0}'
cocoa {appleseed} tool {speechless}

# replace all whole words except 'imp' and 'ant'
$ s='tiger imp goat eagle ant important'
$ echo "$s" | rg --passthru -P '\b(imp|ant)\b(*SKIP)(*F)|\w+' -r '[$0]'
[tiger] imp [goat] [eagle] ant [important]

# recursively match parentheses
$ eqn='(3+a)x * y((r-2)*(t+2)/6) + z(a(b(c(d(e)))))'
$ echo "$eqn" | rg --passthru -P '\((?:[^()]++|(?0))++\)' -r ''
x * y + z

$ # all lowercase letters and optional hyphen combo from start of string
$ s='apple-fig-mango guava grape'
$ echo "$s" | rg --passthru -P '\G([a-z]+)(-)?' -r '($1)$2'
(apple)-(fig)-(mango) guava grape


추출 및 수정


PCRE2 옵션은 -r 옵션이 활성화된 경우에도 사용할 수 있습니다. 아래의 예는 -o 로 하기가 쉽지 않습니다.

$ s='0501 035 154 12 26 98234'

# numbers >= 100 and ignore leading zeros
$ echo "$s" | rg -woP '0*+(\d{3,})' -r '"$1"' | paste -sd,
"501","154","98234"


고정 문자열 일치


sed 와 같이 grep 옵션을 사용하면 고정 문자열을 일치시킬 수 있습니다. 모든 검색 및 바꾸기 도구가 제공해야 하는 편리한 옵션입니다.

$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -F '[4]*' -r '2'
2.3/26
foo
5.3-29

-F는 대체 섹션으로 확장되지 않으므로 문자 그대로 표현하려면 -F 문자 대신 $$가 필요합니다.

$ echo 'a.*{2}-b' | rg --passthru -F '.*{2}' -r '+$x\tc'
a+\tc-b
$ echo 'a.*{2}-b' | rg --passthru -F '.*{2}' -r '+$$x\tc'
a+$x\tc-b


여러 줄 일치



또 다른 편리한 옵션은 여러 줄 일치를 활성화하는 $입니다.

$ s='hi there\nhave a nice day\nbye'

# (?s) flag will allow . to match newline characters as well
$ printf '%b' "$s" | rg --passthru -U '(?s)the.*ice' -r ''
hi  day
bye


도스 스타일 입력 처리


-Urg 옵션을 사용하여 도스 스타일 파일에 대한 지원을 제공합니다.

# same as: sed -E 's/\w+(\r?)$/xyz\1/'
# note that output will retain CR+LF as line ending
# similar to the sed solution, this will work for unix-style input too
$ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r 'xyz'
hi xyz
good xyz


GNU sed와 속도 비교


--crlf 의 또 다른 장점은 rg 보다 빠를 가능성이 높다는 것입니다. 방법론적 세부 분석 및 통찰력은 저자의 ripgrep benchmark with other grep implementations 참조.

# for small files, initial processing time of rg is a large component
$ time echo 'aba' | sed 's/a/b/g' > f1
real    0m0.002s
$ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
real    0m0.007s

# for larger files, rg is likely to be faster
# 6.2M sample ASCII file
$ wget 'https://norvig.com/big.txt'
$ time LC_ALL=C sed 's/\bcat\b/dog/g' big.txt > f1
real    0m0.060s
$ time rg --passthru '\bcat\b' -r 'dog' big.txt > f2
real    0m0.048s
$ diff -s f1 f2
Files f1 and f2 are identical

# nearly 8 times faster!!
$ time LC_ALL=C sed -E 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1
real    0m0.725s
$ time rg --no-unicode --passthru -wP '(\w+)(\s+\1)+' -r '$1' big.txt > f2
real    0m0.093s
$ diff -s f1 f2
Files f1 and f2 are identical


sed의 다른 대안



  • rpl — 검색 및 바꾸기 도구, 대화형 모드 및 재귀 모드와 같은 흥미로운 옵션이 있습니다
  • .

  • sd — 간단한 검색 및 바꾸기, Rust에서 구현됨

  • perlruby — 탁월한 명령줄 지원 기능이 있는 프로그래밍 언어
  • 좋은 웹페이지 즐겨찾기