質問: ファイル内の文字列を置き換えるにはどうすればよいですか? 特定の検索基準に基づいてファイル内の文字列を置き換えることは、非常に一般的なタスクです。どうやって 現在のディレクトリのすべてのファイルで文字列fooをbarに置き換えますか? サブディレクトリに対して同じことを再帰的に行いますか? ファイル名が別の文字列と一致する場合にのみ置き換えますか? 文字列が特定のコンテキストで見つかった場合にのみ置き換えますか? 文字列が特定の行番号にある場合は置き換えますか? 複数の文字列を同じ置換で置き換える 複数の文字列を異なる置換で置き換える
回答: ripgrep (コマンド名rg)はgrepツールですが、検索と置換もサポートしています。$ cat ip.txt dark blue and 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 and 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 and light red light orange red sky rgはインプレースオプションをサポートしていないため、自分で行う必要があります$ # -n isn't needed here as output destination is a file $ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt $ cat ip.txt dark red and light red light orange red sky 正規表現の構文と機能については、 rust regexのドキュメント を参照してください。 -pスイッチは pcre2 フレーバーを有効にします。 rgはデフォルトでunicodeをサポートしています。$ # non-greedy quantifier is supported $ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'x' xrk sand band cue combat $ # unicode support $ echo 'fox:αλεπού,eagle:αετός' | rg '\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 '[[:punct:]--[.!?]]+' -r '' hi there! how are you? all fine here. $ # use -p if you need even more advanced features $ echo 'car bat cod map' | rg -p '(bat|map)(*skip)(*f)|\w+' -r '[$0]' [car] bat [cod] map grepのように、-fオプションを使用すると、固定文字列を照合できます。sedも実装する必要があると思う便利なオプションです。$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -f '[4]*' -r '2' 2.3/26 foo 5.3-29 別の便利なオプションは-uは複数行のマッチングを可能にします$ # (?s) flag will allow . to match newline characters as well $ printf '42\nhi there\nhave a nice day' | rg --passthru -u '(?s)the.*ice' -r '' 42 hi day rgは、dosスタイルのファイルも処理できます$ # same as: sed -e 's/\w+(\r?)$/123\1/' $ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r '123' hi 123 good 123 rgのもう1つの利点は、sedよりも高速である可能性が高いことです$ # 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 $ time lc_all=c sed -e 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1 real 0m0.725s $ time rg --no-pcre2-unicode --passthru -wp '(\w+)(\s+\1)+' -r '$1' big.txt > f2 real 0m0.093s $ diff -s f1 f2 files f1 and f2 are identical