O problema é que o mecanismo regexp do sed não vê seu arquivo de entrada nem sua […]
corresponde a uma lista de caracteres Unicode; Em vez disso, ele vê cada um deles como vários bytes independentes. Por exemplo, ele vê •
como três bytes \xe2 \x80 \xa2
e tenta corresponder cada um deles individualmente com [ \xe2 \x80 \x98 \xe2 \x80 \x99 \x22 \xe2 \x80 ... ]
.
Assim, no exemplo mostrado em sua postagem, o regex corresponde e exclui apenas o último byte de cada caractere de pontuação, mas deixa os outros 2 ainda presentes. Isso é o que dá a você um arquivo de saída inválido (não-UTF-8).
Com o GNU sed (testado no 4.5), isso pode ser evitado certificando-se de que o system locale (as variáveis de ambiente $ LANG ou pelo menos $ LC_CTYPE) esteja configurado para um compatível com UTF-8 localidade. Por exemplo:
$ export LANG='C' $ echo '‘test’ “test”' | sed 's/[“”•]/X/g' XX�testXX� XXXtestXXX $ echo '•_test' | sed 's/[•‡]_/X_/' ��X_test $ export LANG='en_US.UTF-8' $ echo '‘test’ “test”' | sed 's/[“”•]/X/g' ‘test’ XtestX $ echo '•_test' | sed 's/[•‡]_/X_/' X_test
(O idioma local não importa. Qualquer localidade UTF-8 funcionará.)
Se isso não funcionar para você, evite […]
completamente e use \(…\|…\|…\)
(ou (…|…|…)
in sed -r), que é uma alternativa de vários caracteres e funcionará independentemente de como esses caracteres acabam sendo interpretado.
$ export LANG='C' $ echo '‘test’ “test”' | sed 's/\(“\|”\|•\)/X/g' ‘test’ XtestX $ echo '•_test' | sed 's/\(•\|‡\)_/X_/' X_test