Чи можу я використовувати "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тому мені не доведеться винаходити колесо тимчасових файлів з тимчасовим режимом.


Самовідповідаючись на це питання, оскільки я не міг знайти жодних результатів для "sed translate символів". Чарівне ключове слово в кінцевому підсумку було "транслітератним", але я подумав, що варто зробити цю функцію максимально легко зрозумілою.
n.st

Що слід пам’ятати, намагаючись реалізувати для цього обхідні шляхи: tr(правильно) ігнорує рекурсію в наборах заміни: echo 'abc' | tr ab bxbxc. Примітивне рішення може цього розрізати, xxcоскільки воно повторно застосовує переклад до символів, які вже були переведені.
n.st

Пов'язане: аналог tr для символів unicode? (GNU sedвсупереч GNU trможе транслітерувати багатобайтові символи)
Stéphane Chazelas

Якщо ви хочете іншої можливості: perl може робити переклад, і -i, і (якщо не давній) багатобайтовий. Не POSIX, але досить поширений.
dave_thompson_085

Відповіді:


24

sedмає yкоманду, яка працює так само tr:

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

yКоманда Частин POSIX sedспецифікації , тому він повинен працювати на будь-якій платформі.

А оскільки це так sed, ви можете змусити його замінити файл з його відредагованою версією, що щадить вам набридливий темп-файловий бізнес (за умови, що ваша реалізація sedпідтримує -iопцію, яка не визначена POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt

@ StéphaneChazelas Дякую, що вказав на це; Я досі не знав про внутрішню роботу. Я відредагував свою відповідь, щоб згадати про це.
n.st

Дякую, це надзвичайно корисно! Я очікував, що він буде працювати у VIM (8.0.1092 на CentOS 7.3), але це не так. Не повинен нічого сед робити, 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на кількох рахунках:

  • йому не потрібно додаткового місця на диску (за винятком деяких розріджених файлів, спеціальних випадків копіювання під час запису)
  • він зберігає номери inode, права власності, дозволи, 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може працювати для вас.


2
Одне з питань spongeполягає в тому, що воно все одно перезаписується, fileякщо trне вдається (наприклад, якщо ви писали, але не читали доступ до нього file)
Stéphane Chazelas

О, це дійсно так; Я цього не очікував. Спасибі.
mindriot

Дивіться cat file >; fileоператора ksh93, який записує вихід у тимчасовий файл, який перейменований у пункт призначення, лише якщо команда успішна (але подобається sed -i, що створює новий файл замість перезапису оригіналу).
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.