@kyanny's blog

My thoughts, my life. Views/opinions are my own.

Perl: 文字列の特定部分以外を他の文字で置き換える

要はこういうことをしたい。

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

ghp_************************************

(これは GitHub の personal access token を例にしているが、このトークンはもう存在しない)

いくつか要求があり、

  • 全部置き換えるとわかりづらいので、一部はそのまま残したい。↑でいうと ghp_ の部分
  • 他の部分を置き換える文字は決め打ちでいい。↑でいうと * でいい
  • ただし、文字数は置き換えの前後で同じ長さを維持したい

残したい文字列のパターンがあらかじめ分かってないとダメなので、汎用的な正規表現は書けなそう?今回は Authentication token format updates are generally available | GitHub Changelog に列挙されてるパターンだけカバーできればよかったので、↓こんな感じで解決した。

echo $pat | perl -pe 'print; s/([^_]*_)(.*)/$1 . "*" x length($2)/e'

きっと sed や awk でできるのだろうし、なんなら bash だけでできるのかもしれないが、GNU と BSD の違いとかで悩みたくなかったので Perl のワンライナーのお世話になった。

最終的にシェル関数を定義しておしまい。

pat-mask() {
  local pat
  if [ -p /dev/stdin ]; then
    pat=$(cat -)
  else
    pat=$1
  fi
  echo $pat | perl -pe 'print; s/([^_]*_)(.*)/$1 . "*" x length($2)/e'
}

当初は↓というのを編み出したが、これに至るまでに Use a variable-width lookbehind if it won’t match more than 255 characters – The Effective Perler というエラーにひっかかって結局そこそこ悩むハメになった((?<=[^_]*_)(.*) みたいな正規表現を書いてて Lookbehind longer than 255 not implemented in regex に抵触した)

echo $pat | perl -pe 'print; s/(?<=_)(.*)/"*" x length($1)/e'