- нумерація рядків
- нумерація з вирівнюванням номеру вправо
- нумерація непустих рядків
- кількість рядків у тексті
3. перетворення тексту і заміни
3.1. перетворити символи нового рядка з формату dos/windows на unix
задача: перетворити комбінацію символів нового рядка в стилі dos/windows (cr/lf) до стилю unix (lf)sed 's/.$//'
команда покладається на те, що кожен текстовий рядок закінчується послідовністю символів cr/lf (не перевіряючи), і що виконується вона в середовищі *nix. прочитавши новий рядок в робочий буфер, sed відкидає кінцевий символ нового рядка (lf). команда шукає останній символ рядка в буфері (/.$/
) і видаляє, не замінюючи нічим (//
).важливо! жодної перевірки, чи справді текст містить переноси в стилі dos/windows, або пошуку саме такої комбінації в тексті — не виконується. тому спосіб цікавий лише академічно: реальні задачі варто вирішувати інакше…
кращий спосіб:
sed 's/^M$//'
передумова — середовище *nix. команда шукає символ переводу рядка cr (^M
), який є останнім в рядку (/^M$/
, і видаляє, не замінюючи нічим (//
).важливо! тут
^M
— не дослівно набрана з клавіатури комбінація дашка і великої літери m, а один символ, який можна ввести з клавіатури в терміналі, набравши спершу ctrl+v, а тоді одразу ctrl+m.найкраща варіація цього способу:
sed 's/\x0D$//'
по суті, та сама команда, в якій замість самого символа cr вказано його код (13) в шістнадцятковій нотації (0x0D
). щоправда, не всі версії sed цей варіант підтримують — потрібна версія gnu sed, але усі версії лінук саме її й використовують.3.2. перетворити символи нового рядка з формату unix на dos/windows
задача: зворотня до попередньої; перетворити символ нового рядка у стилі unix (lf) наприкінці рядка на комбінацію cr/lf у стилі dos/windows.sed "s/$/`echo -e \\\r`/"
передумова — середовище *nix: використано вставку echo -e \r
(кожен з двох останніх символів екрановано), котра є командою оболонки (shell) і видає символ cr, який додається в кінець рядка (/$/
) в робочому буфері — під час виводу рядка у вихідний потік sed автоматично додасть до нього ще символ lf.інший спосіб:
sed 's/$/\r/'
цей спосіб працює в gnu sed, і працює аналогічно — але без необхідності застосовувать команду оболонки: просте \r
позначає символ cr.зауваження. тут я свідомо пропускаю всі способи аналогічного перетворення рядків у середовищі dos/windows, бо мені без потреби, а якщо доведеться — завжди можна пошукати sed one-liners у тенетах.
3.3. прибрати пробіли і табуляцію на початку рядка
задача: прибрати пробіли і символи табуляції на початку кожного рядка тексту.sed 's/^[ \t]*//'
проста команда: шукає будь-яку кількість (*
) пробілів і символів табуляції ([ \t]
) на початку рядка (^
— і заміняє «нічим» (//
), тобто видаляє.приклад:
cat test.txt | sed 's/^[ \t]*//'
результат:Бринь бандура, та й замовкне…
Чом же не заграє?
Стоїть старець під віконцем, —
Чом же не співає?
3.4. прибрати пробіли в кінці рядка
задача: прибрати зайві пробіли в кінці кожного рядка тексту.sed 's/[ \t]*$//'
аналогічно попередньому прикладу — але шаблон в команді заміни (s///
) відбирає пробіли і табуляцію в кінці ($
) рядка.3.5. видалити пробіли на початку і в кінці рядка
задача: об’єднати в одній команді рішення обох попередніх задач.sed 's/^[ \t]*//;s/[ \t]*$//'
можна було б і просто запустити двічі sed… але красивіший спобів — виконати його один раз, вказавши дві команди через крапку з комою, вони виконуватимуться послідовно над вмістом робочого буфера.3.6. відступ зліва
задача: утворити відступ зліва для всього тексту, додавши до кожного рядка по 5 пробілів на початку.sed 's/^/ /'
проста команда заміни: пошук початку рядка в робочому буфері (/^/
) і вставка в цій позиції потрібної кількості пробілів (/ /
).3.7. виключка тексту вправо
задача: виключити текст вправо, вирівнявши кожен рядок до 79-ї позиції.sed -e :a -e 's/^.\{1,78\}$/ &/;ta'
значно складніший приклад. рішення використовує нову опцію (-e
) і дві команди (:
та t
). опція -e
подібно до ;
дозволяє поєднувати кілька послідовних команд sed в одному рядку (і процесі):sed -e <команда 1> -e <команда 2>
команда :
створює мітку, а t
реалізує логіку умовного розгалуження, як у мовах програмування: якщо попередня команда змінила текст в робочому буфері, t
повертає sed до виконання до першої команди після вказаної мітки, якщо з змін не було — перехід не відбувається.отже, логіка команди така: фрагмент
-e :a
створює мітку з ім’ям a; команда s вибирає в робочому буфері фрагмент тексту, який містить не більше 78 будь-яких символів (.{1,78}
) від початку (^
) до кінця ($
), і заміняє його увесь на цей же фрагмент (&), але з пробілом попереду (/ &/
) — таким чином текст в буфері стає довшим на один передній пробіл. наступна команда ta
переходить назад до мітки a
… і так відбувається доти, поки довжина тексту в буфері є не більшою заданої в шаблоні (78 символів) — але як тільки вона стає 79, перехід на мітку a більше не виконується, sed завершує виконання послідовності — і виводить результат у вихідний потік.важливо! для правильної роботи цієї команди її варто об’єднати з видаленням табуляції на початку рядка та кінцевих пробілів:
sed -e 's/^[ \t]*//;s/[ \t]*$//' -e :a -e 's/^.\{1,78\}$/ &/;ta'
3.8. виключка тексту по центру
задача: відцентрувати кожен рядок тексту в рамках простору шириною 79 символів.sed -e :a -e 's/^.\{1,77\}$/ & /;ta'
все аналогічно до попередньому прикладу, за винятком того, що центрувати можна лише ті рядки, котрі хоча б на 2 символи коротші за максимальну ширину (79), а доповнювати пробілами — з обох сторін (/ & /
). і теж варто спершу видаляти передні та кінцеві пробіли й табуляцію.важливо! недолік цього рішення в тім, що — сюрприз! — він генерує кінцеві пробіли. а вони ж насправді зайві. звісно, можна результат роботи команди пропустити ще раз через знайоме видалення кінцевих пробілів, але є елегантніший варіант:
sed -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/'
перша частина зрозуміла — це виключка вправо, а от друга цікава, бо використовує тимчасову змінну, але як красиво використовує! шаблон ( *)\1
(прибрав екрануючі зворотні риски) означає: знайти в тексті першу найбільшу доступну послідовність пробілів (запам’ятавши її в змінній \1
), але так, щоби за нею слідувала… така ж точно (оте \1
) послідовність! і вибрати для наступної операції обидві. що це значить? якщо в тексті є послідовність із п’яти пробілів поспіль — цей шаблон знайде перші два (за ними ж ідуть ще два!), збереже їх в змінну
\1
— і вибере для редагування два+два пробіли, залишивши п’ятий (зайвий) поза увагою.останній фрагмент цієї команди (
/\1/
) — заміна вибраного фрагменту (х+х пробілів) збереженою в \1
половинкою (х пробілів). по суті текстовий рядок спершу виключено вправо, а тоді з передніх пробілів видалено меншу половину.заміна фрагмента тексту
задача: замінити довільний (наприклад, перший або другий) фрагмент в тексті згідно шаблону іншим фрагментом.sed 's/foo/bar/x'
тут foo — фрагмент, який треба знайти, bar — текст для заміни, а x — номер фрагменту (1 або 2 в цьому прикладі). якщо параметр x опустити — матиметься на увазі перший, а якщо вказати g
(від global) — буде замінено всі знайдені за шаблоном фрагменти.важливо! лік фрагментів ведеться не в тексті загалом, а в кожному рядку окремо!
приклад (використано раніше створений тестовий файл):
cat ~/test.txt | sed 's/а/А/'
результат:Бринь бАндура, та й замовкне…
Чом же не зАграє?
Стоїть стАрець під віконцем, —
Чом же не співАє?
інший приклад:cat ~/test.txt | sed 's/а/А/2'
…і результат:Бринь бандурА, та й замовкне…
Чом же не загрАє?
Стоїть старець під віконцем, —
Чом же не співає?
ще один приклад:cat ~/test.txt | sed 's/а/А/g'
…і результат:Бринь бАндурА, тА й зАмовкне…
Чом же не зАгрАє?
Стоїть стАрець під віконцем, —
Чом же не співАє?
для одного допису досить. далі буде!оновлення. аж через рік я взявся пригадувати просту текстову магію для цілком практичної задачки.