Вопрос: sed: Удаление \ r \ r перед \ n в очень большом файле


У меня есть поврежденный файл образа диска (около 27 ГБ), в котором до того, как были вставлены все \ n символы \ r \ r. Я хочу удалить эти \ r \ r перед всеми \ n.

Я пробовал с awk:

awk '{ sub("\r\r$", ""); print }' mangled.raw > image.raw

Но файл кажется слишком большим: «awk: ошибка времени выполнения: из памяти»

Я также пробовал с sed:

sed 's/\r\r$//g' mangled.raw > image.raw

Но здесь выходной файл кажется неполным: размер составляет всего 20 ГБ, а конец mangled.raw содержит много нулевых символов, а конец image.raw содержит содержимое файла. Каким-то образом sed, кажется, останавливается до конца.

Любая идея, как это сделать правильно?


2
2018-03-18 10:19


Источник


Даже если вы получите команду для работы с файлом такого размера, вы наверняка закончите испорченное изображение: в любом месте, где первоначально было «\ r \ r \ n», «\ r \ r 'также будет удалено. В случайных двоичных данных эта последовательность должна повторяться один раз в каждые 16 МБ, поэтому очень подобный в 27GB. - Jaap Eldering
@eldering В моем файле каждый '\ n' был заменен на '\ r \ r \ n'. Это означает, что уже существующая последовательность '\ r \ r \ n' стала '\ r \ r \ r \ r \ n'. Таким образом, замена '\ r \ r \ n' на '\ n' восстанавливает исходное состояние. - Mel
действительно, вы правы. Виноват. - Jaap Eldering


Ответы:


комментарий престареющего может быть правильным - это зависит от того, как произошло коррупция. Если бы это эквивалентно s/\n/\r\r\n/ то это обратимо, но если бы это произошло s/\r*\n/\r\r\n/ то это не так.

В любом случае я бы использовал perl для чего-то вроде этого. В отличие от sed, он был разработан с самого начала, чтобы работать со строками, которые очень длинны и могут содержать NUL и другие нетекстовые символы.

perl -pe 's/\r\r\n/\n/g' mangled.raw > image.raw

Это может съесть много памяти, так как оно все еще читает файл как ряд строк, и могут быть большие сегменты файла без \n который будет рассматриваться как одна «линия». Но если вы читаете его блоками, вы должны быть осторожны, чтобы не пропустить \r\r\n последовательность, которая пересекает границу блока. Как это:

perl -e '
  $/=\65536;
  while(<>) {
    if(/\r\z/) {
      if(length($nextblock=<>)) {
        $_.=$nextblock;
        redo;
      }
    }
    s/\r\r\n/\n/g;
    print;
   }
' mangled.raw > image.raw

Редактировать: Я понял, что вышеупомянутый код застрял бы в бесконечном цикле, если последний байт ввода был \r, Он был обновлен, чтобы правильно обрабатывать этот случай.

Изменить 2: Перл-один вкладыш содержал неправильный символ замены. Он обновлен.


3
2018-03-18 12:21



Я попробовал ваш perl one-liner. У этой проблемы была такая же проблема, как у моего sed-liner: размер финального файла был слишком мал. Однако он потерпел неудачу намного более изящно, чем sed. - Это дало ошибку из памяти, когда sed сделал вид, что все в порядке. Сейчас я пытаюсь реализовать свою блокировку. - Mel
Ваша реализация блока отлично работала! Большое спасибо. Я бы никогда не смог написать этот скрипт. - Mel