Использование файла спула почты для записи в / etc / aliases дает ошибки в EL7

Жиль меня опередил: нет «указателя, указывающего на первую строку файла». Первая строка файла — начало файла — всегда является первым символом файла. (Могут быть неясные, отдельные приложения которые признают такое понятие, но на системном уровне ничего подобного нет.)

Что вы уже знаете:

Команды вида

  • sed '1,6d' имя файла
  • sed -n '7,$p' имя файла
  • tail -n +7 имя файла

(и, возможно, другие варианты) выведет в стандартный вывод все, кроме первых 6 строк имени файла . (Все они, конечно, прочитали весь файл.) Пока мы этим занимаемся,

  • sed -n '1,6p' имя файла
  • sed '7,$d' имя файла
  • head -n 6 имя файла
  • sed '6q' имя_файла

запишет первые 6 строк из имя_файла в стандартный вывод. Первые два могут прочитать или не прочитать весь файл; последние два наверное не будет.

Кроме того,

command input_filename > the_same_filename

не работает, как обсуждалось в Предупреждение относительно «>».

Возможно, вы не знали:

command arguments    1<> filename

открывает имя файла для чтения и записи. без его усечения. Таким образом,

sed '1,6d' filename  1<> the_same_filename

может быть первым шагом к решению, которое вы ищете. Это, вероятно, так близко, как вы собираетесь прийти к удалению первых M строк файла «на месте»; он будет читать файл и перезаписывать его одновременно, без создания другого файла. Если M достаточно мало (или, в частности, если количество байтов в первых M строках достаточно мало), это может прочитать каждый блок файла один раз и записать каждый блок один раз - и вы не можете сделать ничего лучше, чем это.

Только первый шаг?

Я создал этот тестовый файл:

$ cat -n foo
     1  a
     2  bcd
     3  efghi
     4  jklmnop
     5  qrstuvwxy
     6  z0123456789
     7  ABCDEFGHIJKLM
     8  Once upon a midnight dreary, while I pondered, weak and weary,
     9  Over many a quaint and curious volume of forgotten lore—
    10  While I nodded, nearly napping, suddenly there came a tapping,
    11  As of some one gently rapping—rapping at my chamber door.
    12  "'Tis some visitor," I muttered, "tapping at my chamber door—
    13                                    Only this and nothing more."
    14  The quick brown
    15  fox jump over the
    16  lazy dog. Once upon
    17  this midnight dreary,

Этот файл тщательно сконструирован чтобы длины строк (включая новые строки) 2, 4, 6, 8, 10, 12, 14, 63, 57, 63, 58, 62, 63, 16, 18, 20, и 22. Обратите внимание, что первые шесть строк содержат 2+4+6+8+10+12=42 байта. Последние две строки содержат 20+22 байта, что по совпадению (!) тоже 42. (Общий размер файла 504.) Итак,

$ ls -l foo
-rw-r--r-- 1 myusername mygroupname 504 May 18 04:25 foo

$ sed '1,6d' foo 1<> foo

$ ls -l foo
-rw-r--r-- 1 myusername mygroupname 504 May 18 04:32 foo

$ cat -n foo
     1  ABCDEFGHIJKLM
     2  Once upon a midnight dreary, while I pondered, weak and weary,
     3  Over many a quaint and curious volume of forgotten lore—
     4  While I nodded, nearly napping, suddenly there came a tapping,
     5  As of some one gently rapping—rapping at my chamber door.
     6  "'Tis some visitor," I muttered, "tapping at my chamber door—
     7                                    Only this and nothing more."
     8  The quick brown
     9  fox jump over the
    10  lazy dog. Once upon
    11  this midnight dreary,
    12  lazy dog. Once upon
    13  this midnight dreary,

Хорошо, первые шесть строк исчезли. Исходная строка номер 7 («ABCDEFGHIJKLM») теперь является строкой номер 1. Но что это? Файл увеличился с 17 строк до 13. Должно быть 11 (17−6). А последние две строчки («ленивая собака… полночная тоска») встречаются дважды.

Это одна из ловушек оператора 1 — если вы не обрезаете выходной файл, вы не можете получить файл меньшего размера, чем тот, с которого вы начали. В частности, здесь вывод sed '1,6d' foo составляет 462 байта. (504−42, так как первые шесть строк содержат 42 байта), поэтому он перезаписывает первые 462 байта выходного файла — который также является foo. И первые 462 байта foo — это все, кроме последних 42 (504−462) — поэтому последние две строки не перезаписываются. Две копии последних двух строк («ленивая собака… полночная тоска») являются результатом sed, за которым следует тот, который остался от исходного содержимого файла.

Итак, что дальше?

Все, что нам нужно сделать сейчас, это отбросить последние 42 байта файла. Как оказалось, это можно сделать просто перемещая указатель, указывающий на конец файла. Хорошо, на самом деле это не указатель; это целочисленный размер файла — горшок Ато, горшок Ато. За последние 20 или 30 лет, Unix позволяет обрезать файл до нужного размера, оставив данные до этого момента нетронутыми, и отбрасывание данных после этой точки.

Старая команда, которая сделает это, называется

dd if=/dev/null bs=462 seek=1 of=foo 2> /dev/null

, которая копирует /dev/null поверх foo, начиная с байта 462. Да, это какой-то клуге. Более новая команда, выполняющая эту функцию, —

truncate -s 462 foo

. Она может присутствовать не во всех системах; это не указано в POSIX.

Итак, собирая все вместе,

#!/bin/sh
filename="$1"
bytes_to_remove=$(sed '6q' "$filename" | wc -c)
total_size=$(stat -c '%s' "$filename")
sed '1,6d' "$filename" 1<> "$filename"
new_size=$((total_size - bytes_to_remove))
truncate -s "$new_size" "$filename"

Мы используем wc -c для подсчета символов в первых шести строках. (созданный sed '6q'), вычтите это из общего размера файла, и обрезать файл до этого размера.Вы можете использовать любую из альтернативных команд для вывода первых M строк или последних N−M строк, и вы можете заменить последнюю строку на

dd if=/dev/null bs="$new_size" seek=1 of="$filename" 2> /dev/null

Предостережения:

Я не проверял это на файлах с окончаниями строк

  • CR-LF или
  • многобайтовыми символами,

, и это может быть проблематично.

1
16.05.2016, 20:46
0 ответов

Теги

Похожие вопросы