- Что быстрее удалить первую строку в файле. sed или tail?
- 6 ответов
- Производительность sed против tail убрать первую строку файла
- TL;DR
- эксперимент
- Общие препараты:
- Сроки больших файлов:
- Настройка огромного testfile :
- Выполните запуски по времени с нашим огромным testfile :
- Сроки небольших файлов:
- Настройка небольшого testfile :
- Выполните запуски по времени с нашим маленьким testfile :
- tail решение:
- cat / скобное решение:
- sed в linux — примеры использования
- Примеры
- Фильтрация строк
- Замена по шаблону
- Удаление строк
- Извлечение подстрок
Что быстрее удалить первую строку в файле. sed или tail?
В этом ответе ( Как я могу удалить первую строку файла с помощью sed?) Есть два способа удалить первую запись в файле:
sed '1d' $file >> headerless.txt
tail -n +2 $file >> headerless.txt
Лично я думаю, что tail вариант косметически более приятен и более читабелен, но, вероятно, потому, что я бросил ему вызов.
Какой метод самый быстрый?
6 ответов
Производительность sed против 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 со случайной длиной строки и случайными данными, использующими эту команду (обратите внимание, что она определенно не оптимальна, она становится действительно медленной для>2M строк, но кого это волнует, это не то, что мы анализируем):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
О, кстати. мой тестовый ноутбук работает под управлением Ubuntu 16.04, 64-разрядная на процессоре Intel i5-6200U. Просто для сравнения.
Сроки больших файлов:
Настройка огромного testfile :
Выполнение команды выше с numoflines=10000000 произвел случайный файл, содержащий 10M строк, занимающих чуть более 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 ; do sed '1d' testfile > /dev/null; done real 3m36.756s user 3m19.756s sys 0m15.792s $ time for i in ; do tail -n +2 testfile > /dev/null; done real 0m14.573s user 0m1.876s sys 0m12.420s
Вывод остается прежним, sed неэффективно удалить первую строку большого файла, tail следует использовать там.
И да, я знаю, что циклические конструкции Bash медленны, но мы делаем здесь относительно мало итераций, и время, которое занимает простой цикл, не является значительным по сравнению с sed / tail время выполнения в любом случае.
Сроки небольших файлов:
Настройка небольшого testfile :
Теперь для полноты давайте рассмотрим более распространенный случай, когда у вас есть небольшой входной файл в диапазоне КБ. Давайте создадим случайный входной файл с 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 ; do sed '1d' testfile > /dev/null; done real 0m7.811s user 0m0.412s sys 0m7.020s $ time for i in ; do tail -n +2 testfile > /dev/null; done real 0m7.485s user 0m0.292s sys 0m6.020s
Как видите, сроки очень похожи, толковать или удивлять особо нечего. Для небольших файлов оба инструмента одинаково хорошо подходят.
Вот еще один вариант, используя только встроенные команды Bash и cat :
$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 / скобное решение:
Хотя сейчас у меня есть только Ubuntu VM, и я видел значительные различия во времени обоих, хотя они все находятся на одном уровне.
Попытка в моей системе и префикс каждой команды с time Я получил следующие результаты:
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 КБ. Файл был прочитан с жесткого диска.
Нет объективного способа сказать, что лучше, потому что sed а также tail не единственные вещи, которые запускаются в системе во время выполнения программы. Множество факторов, таких как дисковый ввод-вывод, сетевой ввод-вывод, прерывания процессора для процессов с более высоким приоритетом — все это влияет на скорость выполнения вашей программы.
Они оба написаны на C, так что это не языковая проблема, а скорее экологическая. Например, у меня есть SSD, и в моей системе это займет время в микросекундах, но для того же файла на жестком диске это займет больше времени, потому что жесткие диски значительно медленнее. Таким образом, аппаратная часть играет роль в этом тоже.
Есть несколько вещей, которые вы можете иметь в виду при выборе команды:
- Какова ваша цель? sed потоковый редактор для преобразования текста. tail для вывода определенных строк текста. Если вы хотите разобраться со строками и только распечатать их, используйте tail , Если вы хотите редактировать текст, используйте sed ,
- tail имеет гораздо более простой синтаксис, чем sed так что используйте то, что вы можете прочитать сами, а что могут читать другие.
Другим важным фактором является объем данных, которые вы обрабатываете. Небольшие файлы не дадут вам никакой разницы в производительности. Картина становится интересной, когда вы имеете дело с большими файлами. С 2 ГБ BIGFILE.txt мы можем видеть, что 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
sed в linux — примеры использования
Утилита sed это мощный потоковый редактор текста с поддержкой регулярных выражений. С помощью sed вы можете заменять шаблоны текста (причем непосредственно в файле!), удалять строки (элементы массива), выводить подходящие по маске строки (подобно grep). Редактор sed поддерживает применение нескольких команд и расширенный синтаксис регулярных выражений (при котором не нужно экранировать спец. символы).
Важно!
В sed нет поддержки опережающих и ретроспективных проверок в регулярках! Для замены с использованием расширенного синтаксиса regex используйте:
find . -type f -name '*.blade.php' -exec perl -p -i -e 's/(? \;
Внимание!
В sed довольно проблемно работать с символом перевода строки! Самое удобное решение — это:
sed [-opt] 's/regex/replace/flag' input-file sed 's/regex/replace/flag' # замена найденных подстрок sed '1,5s/regex/replace/gi' # замена только в указанном диапазоне строк sed -r 's/regex/replace/g' # расширенный синтаксис regex (со спец символами) sed 's/regex//g' # удалить найденные подстроки sed '/regex/d' # удалить строки подходящие по маске sed -n 2p # вывести 2ю строку sed -n '/composer/p' # вывести только строки подходящие по маске sed 's/1-9/&/p' # & при замене означает сам ОБРАЗЕЦ
В качестве разделителей можно использовать любые символы (напрмиер: # , @ ). Match части (которые внутри круглых скобок) доступны как \1 , \2 , \n .
-p вывести на экран -d удалить -i выполнять изменения непосредственно в файле -n не выводить результат замены/поиска на экран (--quiet, --silent) -e указывает на передачу инструкции (команда замены/удаления или выражение для поиска/фильтрации). Нужен, если Вы передаете более 1 инструкции -E расширенный regex, ближе к JavaScript, Go. Активны спец символы: 5+ -r расширенный regex синтаксис. Спец символы активны по умолчанию (--regexp-extended) -P perl-совместимый regex синтаксис -s consider files as separate rather than as a single continuous long stream (--separate)
Флаги строки-команды (указывать в конце маски):
g глобальный поиск/замена, а не только первое совпадение i,-l регистро-независимый поиск p, печать найденных подстрок d удалить строки
Примеры
Фильтрация строк
Вывести файлы соответствующие маске:
Строки длиннее 80 символов:
sed -n '/^./p' sed -n '/^./!p' # короче 80 символов
Замена по шаблону
Заменить строки начинающиеся с:
sed 's/^line_start=.*$/line_start="replacement"/' file
Вывести вхождения (matches) через табуляцию:
Заменить названия файлов (composer на composer-dev):
echo 'aa,bb,xx' | sed "s/xx/cc/g" # aa,bb,cc
Заменить URL в файле (штука в разделителях | , и -i для замены в файле):
sed -i "s|$old_site_url|$new_site_url|g" file.yml
Заменить параметр в конфиге:
sed -ie '/project_file_path *=/ s|=.*$|=/home/pi/apps|' ~/.config/geany/geany.conf
Заменить значение в xml-конфиге:
sed -i -r 's/(name="width" value=")[^"]+"/\148KP"/' /etc/ImageMagick-6/policy.xml
Удалить начальные пробелы (аналог ltrim):
echo " some string" | sed 's/^ *//g'
echo " test " | xargs echo " test " | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
Удалить часть приглашения командной строки:
Удаление строк
Удалить из файла строку подходящую шаблону:
Удалить первую строку вывода:
sed 1d # удалить первую строку sed '5, 10d' # удалить строки c 5-й по 10-ю
Удалить строки от первой до соответствующей regex:
echo '--some string' | sed 's/\(Some\)/New \1/i' # --New some string
Примечание
По умолчанию необходимо экранировать все спец. символы в regex’ах, что крайне затрудняет чтение масок. Для того, чтобы экранировать спец.символы только в случае описания в тексте их самих — включите расширенный режим regex выражений с помощью опции -r .
Удалить последние N=2 символа:
Извлечение подстрок
Вырезать / запомнить последние N=4 символа:
echo "latest" | sed "s/.*\(. $\)/\1/" # test