Удалить первые n байтов файлов


32

У меня экстремальная проблема, и все решения, которые я могу себе представить, сложны. Согласно моему опыту работы с UNIX / Linux, должен быть легкий путь.

Я хочу удалить первые 31 байт каждого файла в /foo/. Каждый файл достаточно длинный. Ну, я уверен, что кто-нибудь предложит мне удивительно простое решение, которое я просто не могу себе представить. Может быть, awk?


2
Любое решение awk / sed / ed будет ориентировано на строки, поэтому, если вы не знаете, что первая строка будет содержать не менее 31 символа, возникнут сложности.
Гленн Джекман

Ответы:


28
for file in /foo/*
do
  if [ -f "$file" ]
  then
    dd if="$file" of="$file.truncated" bs=31 skip=1 && mv "$file.truncated" "$file"
  fi
done

или быстрее, благодаря предложению Жиля:

for file in /foo/*
    do
      if [ -f $file ]
      then
        tail +32c $file > $file.truncated && mv $file.truncated $file
      fi
    done

Примечание: хвост Posix указывает «-c +32» вместо «+ 32c», но хвосту Solaris по умолчанию это не нравится:

   $ /usr/bin/tail -c +32 /tmp/foo > /tmp/foo1
    tail: cannot open input

/usr/xpg4/bin/tail хорошо с обоими синтаксисами.


1
Предлагать ddздесь излишне, tailболее уместно (проще, меньше риск опечатки, нет ложных сообщений на stderr).
Жиль "ТАК - перестань быть злым"

Вы правы. Я обычно избегаю команд, предназначенных для обработки текстовых файлов при обработке, возможно, двоичных файлов, но здесь будет работать «tail + 32c».
Jlliagre

1
@jlliagre: Вы написали cut (разве это не должно быть хвостом? ... Асис, это не работает для меня ...
Peter.O

Конечно, это хвост. Извините за несоответствие.
Jlliagre

@jlliagre: На Солярисе вы должны /usr/xpg4/binопередить /usr/binсвоего PATH, иначе вы застрянете в начале 1990-х. Многие юниты (например, GNU, BusyBox) больше не поддерживают исторический +32cсинтаксис и воспринимают его как файл, называемый +32c(как требует POSIX).
Жиль "ТАК - перестань быть злым"

12

Следующие команды обрезают первые 31 байт из $file(используя $file~как временную копию):

dd if="$file" of="$file~" bs=1 skip=31
mv "$file~" "$file"

Вам нужно только перечислить или findвсе файлы в /foo/и выполнить два выше для каждого $fileнайденного.


1
Обмен значениями bs и skip увеличит производительность.
Jlliagre

10

tail -c +32выводит свой ввод минус первые 31 байт. (Да, аргумент отключен на единицу.) Чтобы отредактировать файл на месте, используйте губку в цикле, или, если у вас его нет и вы не хотите беспокоиться, выполните его работу в оболочке:

for x in /foo/*; do tail -c +32 "$x" | sponge "$x"; done
for x in /foo/*; do tail -c +32 "$x" >"$x.new" && mv "$x.new" "$x"; done

Если команды прерваны по какой-либо причине (например, сбой питания), может быть трудно определить, где вы остановились. Запись новых файлов в отдельный каталог упростит задачу.

mkdir /foo.tmp
cd /foo
for x in *; do tail -c +42 -- "$x" >"/foo.tmp/$x" && rm -- "$x"; done
mv /foo.tmp/* /foo
rmdir /foo.tmp

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


2

Вы можете использовать Vim в режиме Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % выбрать все строки

  2. ! Команда Run

  3. x сохранить и закрыть

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.