Який швидше видалити перший рядок у файлі ... sed або tail?


14

У цій відповіді ( Як я можу видалити перший рядок файлу з sed? ) Є два способи видалення першої записи з файлу:

sed '1d' $file >> headerless.txt

** ---------------- АБО ---------------- **

tail -n +2 $file >> headerless.txt

Особисто я вважаю, що цей tailваріант косметично приємніший і легший для читання, але, мабуть, тому, що мені непросто.

Який метод найшвидший?


5
Не відповідь, але можливе враження, що sedце портативніше: "+2" для tailвідмінної роботи на Ubuntu, який використовує GNU tail, але не працює на BSD tail.
Джон N

@JohnN дякую за те, що поділився tailвідсутністю сумісності між платформами.
WinEunuuchs2Unix

3
@John N "+2" для хвоста працює чудово на травні Mac під управлінням Sierra, який претендує на використання хвостової команди BSD
Нік Сілліто

Урх, ти абсолютно прав - я щойно перезапустив його і цього разу перевірив вхід. Що я мав би зробити перший раз. Це також POSIX. / відшаровується, бентежить.
Джон N

2
@JohnN Ви не зовсім помиляєтесь. Раніше UNIX не забезпечував цей -nпараметр і використовував синтаксис tail +2 $file. Дивіться freebsd.org/cgi/… Можливо, ви думали про це, а не про один із сучасних BSD.
hvd

Відповіді:


28

Продуктивність sedvs. tailдля видалення першого рядка файлу

TL; DR

  • sed дуже потужний і універсальний, але саме це робить його повільним, особливо для великих файлів з багатьма рядками.

  • tail робить лише одну просту річ, але ця вона робить добре і швидко, навіть для великих файлів з багатьма рядками.

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

Експеримент

Загальна підготовка:

Наші команди для аналізу:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

Зауважте, що я /dev/nullкожного разу пересилаю висновки, щоб усунути вихідний термінал або запис файлів як вузьке місце.

Давайте встановимо диск RAM для усунення дискового вводу / виводу як потенційного вузького місця. У мене особисто є tmpfsучасник, /tmpтому я просто розмістив testfileтам свій експеримент.

Тоді я один раз створюю випадковий тестовий файл, що містить задану кількість рядків $numoflinesіз випадковою довжиною рядка та випадковими даними, використовуючи цю команду (зауважте, що це точно не оптимально, він стає дійсно повільним для приблизно> 2М рядків, але кого це цікавить, це не що ми аналізуємо):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

Ой, дощо. мій тестовий ноутбук працює на Ubuntu 16.04, 64 біт на процесорі Intel i5-6200U. Просто для порівняння.

Час великих файлів:

Налаштування величезного testfile:

Запустити команду вище із numoflines=10000000створеним випадковим файлом, що містить 10М рядків, займає трохи більше 600 Мб - це досить величезно, але почнемо з нього, тому що ми можемо:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

Виконайте приурочений пробіг із нашими величезними testfile:

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

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

Ми вже бачимо дійсно зрозумілий результат для великих файлів, tailце на швидкість швидше, ніж sed. Але просто для розваги і щоб бути впевненим, що випадкові побічні ефекти не мають великої різниці, давайте зробимо це 100 разів:

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

Висновок залишається таким же, sedнеефективний для видалення першого рядка великого файлу, його tailслід використовувати там.

І так, я знаю, що конструктивні петлі Баша повільні, але ми тут робимо лише мало ітерацій, і час, який звичайний цикл займає, не є істотним порівняно з sed/ час tailвиконання.

Визначення часу:

Налаштування малого testfile:

Тепер для повноти розглянемо більш поширений випадок, коли у вас є невеликий вхідний файл у діапазоні kB. Давайте створимо випадковий вхідний файл за допомогою такого numoflines=100вигляду:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

Виконайте приурочений пробіг із нашими маленькими testfile:

Оскільки ми можемо очікувати, що час роботи таких невеликих файлів буде в межах декількох мілісекунд від досвіду, давайте відразу зробимо 1000 ітерацій:

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

Як бачите, таймінги досить схожі, тлумачити чи дивуватися не так вже й багато. Для невеликих файлів обидва інструменти однаково добре підходять.


+1 за відповідь дякую. Я редагував оригінальне запитання (вибачте) на основі коментаря Серга, який також awkможе це зробити. Моє оригінальне запитання ґрунтувалося на посиланні, яке я знайшов в першу чергу. Після всієї вашої наполегливої ​​роботи, будь ласка, порадьте, чи варто мені зняти awkкандидата на рішення та повернути увагу до початкового обсягу проекту лише sedта tail.
WinEunuuchs2Unix

Яка це система? На моєму mac (тому інструменти BSD) тестування на / usr / share / dict / words дає мені 0,09 секунди для sed і 0,19s для хвоста (і awk 'NR > 1', що цікаво).
Кевін

5

Ось ще одна альтернатива, використовуючи лише bash вбудовані та cat:

{ read ; cat > headerless.txt; } < $file

$fileперенаправляється в { }групу команд. readПросто читає і скидає перший рядок. Потім решта потоку проходить трубопроводом, catякий записує його у файл призначення.

На моєму Ubuntu 16.04 продуктивність цього та tailрішення дуже схожі. Я створив великий тестовий файл із seq:

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail рішення:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/ брекет-рішення:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

Наразі у мене є лише зручна програма Ubuntu VM, і я помітив суттєві зміни в термінах обох, хоча вони всі в одному і тому ж бальному парку.


1
+1 за відповідь, дякую. Це дуже цікаве рішення, і я люблю дужки і праворуч на читання через ієрархічний наказ Баша. (не впевнений, чи правильно я це сформулював). Чи можливо оновити свою відповідь розміром вхідного файлу та результатами порівняння часу, якщо це досить просто?
WinEunuuchs2Unix

@ WinEunuuchs2Unix додано терміни, хоча вони не надто надійні, оскільки це на VM. Зараз у мене немає зручності установки металевого Ubuntu.
Цифрова травма

Я не думаю, що VM проти Bare Metal має значення, коли ти порівняєш VM з VM. Дякую за підтвердження часу. Я б, мабуть, пішов, tailале все ще думаю, що readваріант дуже класний.
WinEunuuchs2Unix

4

Спробувавшись у своїй системі та встановивши префікс кожної команди, timeя отримав такі результати:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

і хвіст:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

що дозволяє припустити, що в моїй системі принаймні AMD FX 8250 під керуванням Ubuntu 16.04 хвіст значно швидший. Тестовий файл мав 10 000 рядків розміром 540 к. Файл читався з жорсткого диска.


+1 за відповідь дякую. В окремому тесті в AU Chatroom один користувач показав, що хвіст в 10 разів швидше (2,31 секунди), ніж sed (21,86 секунди), використовуючи RAMDisk з файлом 61 Мб. Я відредагував вашу відповідь, щоб застосувати блоки коду, але ви, можливо, захочете її відредагувати відповідно до використовуваного розміру файлу.
WinEunuuchs2Unix

@Serg Абсолютно справедливо, що це лише анекдотична відповідь, і, можливо, ви отримаєте різні результати з різною конфігурацією апаратних засобів, різними тестовими файлами тощо
Nick Sillito

2
Файл, який не знаходиться в кеш-пам'яті, при використанні sedможе відігравати фактор цього результату, саме такий порядок ви перевірили.
Minix

яка система? Коли я коментував інший пост тут, на моєму mac sedбуло приблизно вдвічі швидше.
Кевін

1

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

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

Ви можете пам’ятати про кілька речей, які слід пам’ятати, яку команду вибрати:

  • Яке ваше призначення? sedє редактором потоку для перетворення тексту. tailпризначений для виведення конкретних рядків тексту. Якщо ви хочете мати справу з рядками і лише роздруковувати їх, використовуйте tail. Якщо ви хочете відредагувати текст, використовуйте sed.
  • tailмає набагато простіший синтаксис, ніж те sed, тому використовуйте те, що ви можете прочитати самостійно, і те, що можуть прочитати інші.

Ще одним важливим фактором є кількість даних, які ви обробляєте. Невеликі файли не дають вам різниці в продуктивності. Зображення стає цікавим, коли ви маєте справу з великими файлами. З BIGFILE.txt розміром 2 ГБ ми бачимо, що sedсистемні виклики мають набагато більше, ніж tailі працюють значно повільніше.

bash-4.3$ du -sh BIGFILE.txt 
2.0G    BIGFILE.txt
bash-4.3$ strace -c  sed '1d' ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 59.38    0.079781           0    517051           read
 40.62    0.054570           0    517042           write
  0.00    0.000000           0        10         1 open
  0.00    0.000000           0        11           close
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        19           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         7         7 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.134351               1034177        11 total
bash-4.3$ strace -c  tail  -n +2 ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.30    0.148821           0    517042           write
 37.70    0.090044           0    258525           read
  0.00    0.000000           0         9         3 open
  0.00    0.000000           0         8           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0        10           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.238865                775615         7 total

+1 за відповідь дякую. Але я не впевнений, що цей коментар допомагає мені вирішити, яку команду я повинен використовувати ....
WinEunuuchs2Unix

@ WinEunuuchs2Unix Ну, ви запитали, яка команда краще, тому я відповідаю саме на це питання. Яку команду вибрати, залежить від вас. Якщо ти вмієш читати tailкраще sed- використовуй це. Я особисто хотів би використовувати pythonабо , awkа не sedтому , що він може отримати комплекс. Крім того, якщо вас турбує продуктивність, давайте поглянемо на реальність - тут ви бачите результати в мікросекундах. Ви не відчуєте різниці, якщо це не чудовий величезний файл у діапазоні гігабайт, який ви намагаєтесь прочитати
Сергій Колодяжний

Ой, я також вдячний awkвідповіді:) ... Моє запитання грунтувалося на ще одному запитанні AU (за посиланням), і там вони ніколи не згадували awk. Я згоден, різниця в часі номінальна для невеликих файлів. Я просто намагався виробити деякі хороші звички.
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix Звичайно, тут: awk 'NR!=1' input_file.txt . Це дає мені однаковий результат, приблизно 150 мілісекунд, однакове число і для, tailі для sed. Але по-азіатськи я використовую SSD, тому я б сказав, що це жорсткий диск і процесор, а не команда.
Сергій Колодяжний

1
@Serg, навіть маючи лише 60 МБ файл, що містить 1М рядків, 1000 працює sedза тривалістю більше 3 хвилин, тоді як tailпотрібно лише близько 20 секунд. Це не що велике поки на самому ділі, безумовно , не в діапазоні ГБ.
Байт-командир

1

Верхня відповідь не враховувала диск > /dev/null

якщо у вас великий файл і ви не хочете створювати тимчасовий дублікат на своєму диску, спробуйте vim -c

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

Редагувати: якщо файл більше, ніж наявна пам'ять vim -c, не працює, схоже, його недостатньо розумно, щоб зробити додаткове завантаження файлу


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.