Додайте рядки до початку та кінця величезного файлу


23

У мене є сценарій, коли рядки потрібно додавати на початку та в кінці величезних файлів.

Я спробував, як показано нижче.

  • для першого рядка:

    sed -i '1i\'"$FirstLine" $Filename
  • для останнього рядка:

    sed -i '$ a\'"$Lastline" $Filename  

Але проблема з цією командою полягає в тому, що вона додає перший рядок файлу і проходить весь файл. Для останнього рядка знову проходить весь файл та додається останній рядок. Оскільки дуже великий файл (14 Гб), це займає дуже багато часу.

Як я можу додати рядок до початку та іншого до кінця файла, читаючи файл лише один раз?

Відповіді:


20

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

Якщо перезапис файлу не є опцією, ви можете розглянути можливість маніпулювання ним під час читання, наприклад:

{ echo some prepended text ; cat file ; } | command

Також sed призначений для редагування потоків - файл не є потоком. Використовуйте програму, яка призначена для цієї мети, як-от ed або ex. -iВаріант СЕД не тільки не портативний, він буде також порушувати будь-які символічні посилання на файл, так як він по суті видаляє його і відтворює його, що НЕ має сенсу.

Ви можете зробити це в одній команді edтак:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

Зауважте, що залежно від вашої реалізації ed, він може використовувати файл підкачки, вимагаючи, щоб у вас було принаймні стільки місця.


Привіт, Ed команда, яка надана, працює дуже добре для величезних файлів. Але у мене є 3 величезні файли, такі як Test, Test1, Test 2. Я дав команду, як ed -s Tes * << 'EOF' 0a, перед цим рядком додати рядки. $ a додайте ці рядки до кінця. w EOF Але він бере лише тестовий файл і додає перші / останні рядки. Як ми можемо внести зміни в одну команду, щоб вона мала додати перший і останній рядок у всі файли.
UNIXбест

@UNIXbest - Використовуйте forцикл:for file in Tes*; do [command]; done
Chris Down

Привіт вниз, я використав команду нижче для файлу в Tes *; зробіть ed -s Tes * << 'EOF' 0a HEllO HDR. $ a Привіт TLR. w EOF зроблено Але його все ще записується в перший файл.
UNIXбест

Правильно, тому що потрібно використовувати "$file", а не Tes*як аргумент ed.
Кріс Даун

2
@UNIXbest Якщо ваша проблема була вирішена цією відповіддю, вам слід подумати про її прийняття.
Джозеф Р.

9

Зауважте, що якщо ви не хочете виділяти цілу копію файлу на диск, ви можете зробити:

sed '
1i\
begin
$a\
end' < file 1<> file

Для цього використовується той факт, що коли stdin / stdout є файлом, він sed читає і записує за допомогою блоку. Отже, тут нормально переосмислювати файл, який він читає, поки перший рядок, який ви додаєте, менший за sedрозмір блоку '(має бути на кшталт 4k або 8k).

Зауважте, що якщо з якихось причин sedне вдасться (загинув, збій машини ...), ви закінчите обробку файлу наполовину, що означатиме, що деякі дані розміром першого рядка відсутні десь посередині.

Також зауважте, що якщо ваш sedGNU sed, це не працює для двійкових даних (але оскільки ви використовуєте -i, ви використовуєте sed GNU).


ця помилка для мене на Ubuntu 16.04
Csaba Toth

4

Ось кілька варіантів (усі вони створять нову копію файлу, тому переконайтеся, що у вас є достатньо місця для цього):

  • простий відлуння / кіт

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawk тощо

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkі його хибне читання файлів по черзі. BEGIN{}Блок виконується до першого рядка і END{}блоку після останнього рядка. Отже, команда вище означає print "first" at the beginning, then print every line in the file and print "last" at the end.

  • Perl

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    Це по суті те саме, що і гаук вище, щойно написаний в Perl.


1
Зауважте, що в усіх цих випадках вам потрібно буде принаймні на 14 Гб більше місця для нового файлу.
Кріс Даун

@ChrisDown хороший пункт, я відредагував свою відповідь, щоб зробити це зрозумілим. Я припускав, що це не проблема, оскільки ОП використовував, sed -iякий створює тимчасові файли.
terdon

3

Я віддаю перевагу набагато простіше:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

Це перетворює файл:

asdf
qwer

до файлу:

foo
asdf
qwer
bar

2

Ви можете використовувати Vim в режимі Ex:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 виберіть перший рядок

  2. i вставити текст і новий рядок

  3. $ виберіть останній рядок

  4. a додайте текст та новий рядок

  5. x зберегти і закрити


що робити, якщо ми хотіли зробити це для кількох файлів?
geoyws

1
@geoyws, що насправді не стосується цього питання
Стівен Пенні

ви впевнені, що це $ a, а не% a?
Карлос Роблес

2

Немає можливості вставити дані на початку файлу¹, все, що ви можете зробити, це створити новий файл, записати додаткові дані та додати старі дані. Тому вам доведеться перезаписати весь файл хоча б раз, щоб вставити перший рядок. Останній рядок ви можете додати, не переписуючи файл.

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

Крім того, ви можете об'єднати дві команди в один запуск sed.

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

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

¹ У Linux є спосіб вставити дані у файл, але він може вставити лише цілу кількість блоків файлової системи, він не може вставляти рядки довільної довжини. Це корисно для деяких програм, наприклад, баз даних та віртуальних машин, але марно для текстових файлів.


Неправда. Подивіться fallocate()з FALLOC_FL_INSERT_RANGEдоступні на XFS і ext4 в сучасних ядрах (4.xx) man7.org/linux/man-pages/man2/fallocate.2.html
Eric

@Eric Ви можете вставляти лише цілі блоки, але не довільної довжини байтів, принаймні, як для Linux 4.15.0 з ext4. Чи є файлова система, яка може вставляти довільну довжину байтів?
Жил "ТАК - перестань бути злим"

Правильно, але це все ще не робить вашу заяву правильною. Ви писали: "Немає можливості вставити дані на початку файлу". Це все ще не відповідає дійсності: існує механізм для вставки розширень на початку файлу. Він поставляється із застереженнями, але це варто згадати, оскільки деякі користувачі можуть не піклуватися про обмеження розміру блоку, заповнюючи пробіли або повертаючи перевезення.
Ерік

0
$ (echo "Some Text" ; cat file1) > file2

4
Лише кодова відповідь неприйнятна. Вдосконаліть свою відповідь
Networker

Спробуйте розширити свою відповідь, щоб включити пояснення вашої пропозиції або посилання на документацію, яка підтримує ваше рішення.
HalosGhost

-1

Сучасні ядра Linux (вище 4,1 або 4,2) підтримують введення даних на початку файлу через fallocate()системний виклик із FALLOC_FL_INSERT_RANGEфайловими системами ext4 та xfs. По суті це логічна операція зсуву: дані логічно переміщуються при більш високому зміщенні.

Існує обмеження щодо деталізації діапазону, який ви хочете вставити на початку файлу. Але для текстових файлів ви, мабуть, можете виділити трохи більше, ніж потрібно (до межі деталізації) та заповнити пробіли або повернення каретки, але це залежить від вашої програми

Я не знаю жодної легкодоступної утиліти Linux, яка маніпулює розширеннями файлів, але написати це не складно: отримайте дескриптор файлу та зателефонуйте fallocate()з відповідними аргументами. Детальнішу інформацію див. На головній сторінці fallocateсистемного виклику: http://man7.org/linux/man-pages/man2/fallocate.2.html


Утиліта не є проблемою (якщо припустити, що не вбудований Linux): util-linux містить fallocateутиліту. Проблема полягає в тому, що деталізація цілих блоків робить це непотрібним для більшості текстових файлів. Інша проблема полягає в тому, що розподіл діапазону та наступні модифікації не є атомними. Тому це насправді не вирішує проблему.
Жил "ТАК - перестань бути злим"

Детальність - це застереження, про яке я вже згадував, і ні, це не робить його марним, це залежить від застосування. Де ви побачили в питанні, що атомність важлива? Я бачу лише проблему виступів. Незважаючи на це, цей системний виклик здається атомним: elixir.bootlin.com/linux/latest/source/fs/open.c#L228, і якщо атомність стає важливою (це не так, але скажіть, що це заради аргументу), тоді просто використовуйте блокування файлів. (вкажіть мені на місце в коді ядра, де fallocateатомність порушена, будь ласка, мені цікаво)
Ерік,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.