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, кажется, останавливается до конца.

Есть идеи, как это сделать правильно?

1 ответ

Решение

Комментарий старейшины может быть правильным - это зависит от того, как произошла коррупция. Если бы это было эквивалентно 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: однострочник Perl содержал неправильный символ замены. Это было обновлено.

Другие вопросы по тегам