Є всілякі причини, чому читання цілого файлу в просторі шаблонів може піти не так. Логічна проблема у питанні навколо останнього рядка є загальною. Це пов'язано з sed
циклом ліній "Росія" - коли більше немає рядків і sed
зустрічається EOF, він проходить - він припиняє обробку. І тому, якщо ви перебуваєте на останньому рядку, і ви даєте вказівку sed
отримати інший, він зупиниться прямо там і більше не робитиме.
Це означає, що якщо вам дійсно потрібно прочитати цілий файл у просторі шаблонів, то, мабуть, варто все-таки розглянути інший інструмент. Справа в тому, що sed
це однойменний редактор потоків - він призначений для роботи рядка - або логічного блоку даних - за раз.
Є багато подібних інструментів, які краще оснащені для обробки повних файлових блоків. ed
і ex
, наприклад, можна зробити багато чого, що sed
можна зробити, і з подібним синтаксисом - і багато іншого крім того, - але замість того, щоб працювати лише на вхідному потоці, перетворюючи його на вихід, як sed
і вони, вони також підтримують тимчасові файли резервного копіювання у файловій системі . Їх робота буферизована на диску, якщо це потрібно, і вони не виходять різко в кінці файлу (і, як правило, імплодують набагато рідше під напругою буфера) . Більше того, вони пропонують багато корисних функцій, які sed
не мають такого роду, які просто не мають сенсу в потоковому контексті - наприклад, позначки рядків, скасування, названі буфери, приєднання тощо.
sed
Основна сила - це його здатність обробляти дані, як тільки вони їх читають - швидко, ефективно та в потоці. Коли ви робите файл, ви викидаєте це, і ви, як правило, стикаєтеся з проблемами кращого регістру, як, наприклад, згадана вами проблема останнього рядка, і перевищення буфера, і бездоганна продуктивність - оскільки дані, які він аналізує, зростають у довжину, час обробки двигуна regexp при перерахуванні збігів зростає експоненціально .
Що стосується останнього пункту, до речі: хоча я розумію, що цей приклад s/a/A/g
, швидше за все, є лише наївним прикладом і, мабуть, не є власне сценарієм, який ви хочете зібрати для введення, можливо, вам варто поцікавитися, аби ознайомитись y///
. Якщо ви часто g
виявляєте, що замінюєте одного символу на іншого, то це y
може бути дуже корисним для вас. Це перетворення на відміну від заміни і відбувається набагато швидше, оскільки не передбачає повторного відтоку. Цей останній пункт також може бути корисним при спробі збереження та повторення порожніх //
адрес, оскільки це не впливає на них, але може на них вплинути. У будь-якому випадку, y/a/A/
є більш простий засіб здійснити те саме - і підміна можлива так само, як:y/aA/Aa/
який би міняв усі верхні / малі регістри, як на лінії один для одного.
Також слід зауважити, що поведінка, яку ви описуєте, насправді не є тією, що повинна так чи інакше відбуватися.
З GNU info sed
в розділі ЗВІТНІ ЗВІТНІ ЧАСТИ :
POSIXLY_CORRECT
Мінлива оточення згадується , тому що POSIX специфицирует , що якщо sed
зустрічає EOF при спробі N
він повинен кинути курити без виходу, але версія GNU навмисно порушує зі стандартом в цьому випадку. Зауважте також, що навіть як поведінка виправдана вище припущення, що випадок помилки є одним з редагування потоків - не пригнічуючи весь файл в пам'ять.
У стандартних визначає N
«и поведінку , таким чином:
N
Додайте наступний рядок введення, за вирахуванням його закінчувальної \n
лінії виходу, до простору шаблону, використовуючи вбудовану \n
лінію ewline для відокремлення доданого матеріалу від вихідного матеріалу. Зауважте, що номер поточного рядка змінюється.
Якщо наступний рядок введення недоступний, N
командне дієслово повинно розгалужуватися до кінця сценарію та вийти з нього, не запускаючи новий цикл або копіюючи простір шаблону на стандартний вихід.
У цій замітці є деякі інші GNU-ізми, продемонстровані у питанні, зокрема використання дужок :
, b
ранчо та {
функціональних контекстів }
. Як правило, будь- sed
яка команда, яка приймає довільний параметр, має розмежувати на \n
ewline у сценарії. Отже команди ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... дуже ймовірно, що вони будуть виконуватись помилково, залежно від того, sed
що їх читає. Портативно їх слід записати:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
Те ж саме справедливо і для r
, w
, t
, a
, i
, і c
(і , можливо , трохи більше , що я забуваю в даний момент) . Майже в кожному випадку вони також можуть бути написані:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... де новий -e
оператор xecution стоїть для \n
роздільника ewline. Тож там, де info
текст GNU передбачає, що традиційна sed
реалізація змусить вас це зробити :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... це швидше повинно бути ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... звичайно, це теж не вірно. Писати сценарій таким чином - трохи нерозумно. Є набагато більш прості засоби зробити те саме, як:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... який друкує:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... тому що t
команда est - як і більшість sed
команд - залежить від циклу рядків, щоб оновити його регістр повернення, і тут цикл рядків дозволено виконувати більшу частину роботи. Це ще один компроміс, який ви робите, коли ви робите файли - цикл рядків більше не оновлюється, і так багато тестів будуть вести себе ненормально.
Наведена вище команда не ризикує перевиконати вхід, оскільки вона просто виконує прості тести, щоб перевірити, що вона читає під час її читання. Зі H
старими всі рядки додаються до місця утримування, але якщо рядок відповідає, /foo/
він замінює h
старий пробіл. Далі буфери x
змінюються, і s///
робиться спроба умовного введення, якщо вміст буфера відповідає //
останньому адресованому шаблону. Іншими словами, //s/\n/&/3p
намагається замінити третій новий рядок у просторі утримування та надрукувати результати, якщо простір утримування наразі відповідає /foo/
. Якщо це t
успішно, сценарій відгалужується до мітки n
ot d
elete - що робить l
дуб і завершує сценарій.
У випадку, якщо /foo/
і третій новий рядок не можуть бути зібрані разом у просторі утримування, тоді //!g
він замінить буфер, якщо /foo/
він не збігається, або, якщо він збігається, він замінить буфер, якщо \n
ewline не збігається (тим самим замінюючи /foo/
на себе) . Цей невеликий витончений тест утримує буфер від заповнення без необхідності на тривалих розтягненнях /foo/
і забезпечує процес залишається швидким, оскільки вхід не накопичується. Після цього у випадку «не» /foo/
або « //s/\n/&/3p
помилка» буфери знову поміняються, і кожен рядок, але останній, видаляється.
Останній - останній рядок $!d
- це проста демонстрація того, як можна створити sed
сценарій зверху вниз, щоб легко обробляти декілька справ. Коли ваш загальний метод полягає в тому, щоб вирізати небажані випадки, починаючи з найбільш загальних і працюючи над самими конкретними, тоді крайові випадки можна легше обробляти, тому що їм просто дозволено потрапляти до кінця сценарію з іншими потрібними даними та коли це все, що вам залишилося, лише ті дані, які ви хочете. Однак, витягнути такі крайові корпуси із замкнутого циклу може бути набагато складніше.
І ось ось останнє, що я маю сказати: якщо ви дійсно повинні витягнути цілий файл, то ви можете стояти, щоб зробити трохи менше роботи, покладаючись на цикл ліній, щоб зробити це за вас. Зазвичай ви будете використовувати N
внутр і n
зовн для випереджаючого перегляду - тому що вони просуваються вперед циклу лінії. Замість того, щоб надмірно реалізувати замкнутий цикл у циклі - оскільки sed
цикл ліній все одно просто простий цикл читання - якщо ваша мета полягає лише в тому, щоб збирати вхід без розбору, то це, мабуть, простіше:
sed 'H;1h;$!d;x;...'
... який збере весь файл або спробує перебрати.
побічна примітка про N
та останню поведінку рядка ...
в той час як у мене немає інструментів, доступних мені для тестування, врахуйте, що N
під час читання та редагування на місці поводиться інакше, якщо редагований файл - це файл сценарію для наступного перегляду.