Могу ли я использовать `sed` для перевода символов, таких как` tr`?

14

Я хотел бы заменить набор символов соответствующими символами из другого набора, примерно так:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

Подобные переводы / транслитерации являются специальностью trкоманды:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

К сожалению tr, не поддерживает изменение файлов на месте, как это sedделает.
Я хотел бы использовать, sedчтобы мне не пришлось заново изобретать колесо жонглирования временными файлами.

n.st
источник
Самостоятельно отвечая на этот вопрос, так как я не могу найти никаких результатов для "sed translate characters". Волшебное ключевое слово оказалось «транслитерируемым», но я подумал, что стоит сделать эту функцию максимально доступной для поиска.
n.st
Что следует иметь в виду, пытаясь реализовать обходные пути для этого: tr(правильно) игнорирует рекурсию в наборах замены: echo 'abc' | tr ab bxbxc. Примитивное решение может привести к этому, xxcпотому что оно повторно применяет перевод к уже переведенным символам.
17
Связанный: tr аналог для символов юникода? (GNU в sedотличие от GNU trможет транслитерировать многобайтовые символы)
Стефан
Если вам нужна другая возможность: perl может сделать translate, а -i и (если не древний) многобайтовым. Не POSIX, но довольно распространенный.
dave_thompson_085

Ответы:

24

sedимеет yкоманду, которая работает так же, как tr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

Команда yявляется частью спецификации POSIXsed , поэтому она должна работать практически на любой платформе.

И поскольку это так sed, вы можете заменить файл на его отредактированную версию, избавив вас от хлопотного бизнеса временных файлов (если ваша реализация sedподдерживает -iопцию, которая не указана в POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt
n.st
источник
@ StéphaneChazelas Спасибо за указание на это; Я не знал о внутренней работе до сих пор. Я отредактировал свой ответ, чтобы упомянуть об этом.
17
Спасибо, это чрезвычайно полезно! Я ожидал, что он будет работать в VIM (8.0.1092 на CentOS 7.3), но это не так. Разве что-нибудь не должно делать sed, VIM?
dotancohen
1
@dotancohen То, что функция замещения Vim смоделирована после sed's', не означает, что другие функции также хороши . ;) В списке рассылки Vim есть ветка о поиске y/abc/def/эквивалента; лучший вариант, кажется :%call setline(".", tr(getline("."),"abc","def")).
n.st
8

Если, как и в вашем случае, вы транслитерируете символы без изменения их размера (в любом случае, некоторые реализации, такие как GNU, trподдерживают только однобайтовые символы), вы можете сделать:

tr 'ots' 'u.x' < file 1<> file

То есть trперезаписать файл поверх себя.

Это лучше, чем sed -iна нескольких счетах:

  • ему не нужно дополнительное дисковое пространство (за исключением некоторых редких файлов, особых случаев копирования при записи)
  • он сохраняет номера инодов, владельца, разрешения, ACL ...
  • с символическими ссылками работает нормально, жесткие ссылки не ломаются
  • это не оставляет временные файлы, лежащие около когда убито.

Один из недостатков заключается в том, что если он прерван, файл в итоге будет переведен наполовину (однако в этом случае вы можете запустить его снова, чтобы завершить его). Некоторые sedреализации справились бы с этим правильно, убедившись, что исходный файл остается неизменным, пока команда не будет выполнена успешно.

Стефан Шазелас
источник
3
Будьте внимательны при повторном запуске перевода, если у вас есть рекурсия в наборах переводов, например echo 'abc' | tr ab bx.
n.st
1
@ n.st, да, именно поэтому я сказал в этом случае , хотя я согласен, что это стоит изложить.
Стефан
В конце концов мне все-таки пришлось работать с временными файлами: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Многобайтовые символы делали невозможным использование GNU, trа в нашей среде PXE, насыщенной символическими ссылками, это было запутаннымsed -i ожиданием случиться…: /
n.st
@ n.st iconv -t cp437кажется более подходящим для этого.
Стефан
iconvпрерывается, когда входной файл уже содержит байты в кодировке cp437 или смесь нескольких кодировок. Поэтому, хотя в общем случае это предпочтительнее, в этом случае более надежно выполнять замену вручную.
n.st
4

В качестве другой альтернативы, если вашей основной проблемой является отсутствие поддержки для изменения файлов на месте, вас может заинтересовать spongeинструмент из пакета moreutils :

tr 'ots' 'u.x' < file | sponge file

будет записывать file, но открывать fileдля записи только после завершения ввода. Из справочной страницы :

spongeчитает стандартный ввод и записывает его в указанный файл. В отличие от перенаправления оболочки, губка впитывает весь свой ввод перед открытием выходного файла. Это позволяет создавать конвейеры, которые читают и пишут в один и тот же файл.

Если у вас нет действительно больших файлов, которые не могут быть сохранены в памяти, они spongeмогут работать на вас.

mindriot
источник
2
Одна из проблем spongeзаключается в том, что он все равно перезаписывается в fileслучае trсбоя (например, если у вас был доступ для записи, но вы не читали file)
Стефан
О, это действительно так; Я этого не ожидал. Благодарю.
Разум
Смотрите cat file >; fileоператор ksh93, который записывает вывод в временный файл, который переименовывается в место назначения, только если команда выполнена успешно (но, например sed -i, это создает новый файл вместо перезаписи оригинала).
Стефан