Usuń pierwsze n bajtów plików

32

Mam skrajny problem, a wszystkie rozwiązania, które mogę sobie wyobrazić, są skomplikowane. Zgodnie z moim doświadczeniem w systemach UNIX / Linux musi istnieć łatwy sposób.

Chcę usunąć pierwsze 31 bajtów każdego pliku w /foo/. Każdy plik jest wystarczająco długi. Cóż, jestem pewien, że ktoś dostarczy mi zaskakująco łatwe rozwiązanie, którego po prostu nie mogę sobie wyobrazić. Może awk?

von der tann
źródło
2
Każde rozwiązanie awk / sed / ed będzie zorientowane na linię, więc jeśli nie wiesz, że pierwsza linia będzie miała co najmniej 31 znaków, wtedy pojawią się komplikacje.
glenn jackman

Odpowiedzi:

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

lub szybciej, dzięki sugestii Gillesa:

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

Uwaga: Ogon Posix określa „-c +32” zamiast „+ 32c”, ale domyślny ogon Solaris nie lubi:

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

/usr/xpg4/bin/tail jest w porządku z obiema składniami.

jlliagre
źródło
1
Sugerowanie ddtutaj jest przesadą, tailjest bardziej odpowiednie (prostsze, mniejsze ryzyko literówki zabójcy, brak fałszywych wiadomości na stderr).
Gilles „SO- przestań być zły”
Masz rację. Zwykle unikam poleceń przeznaczonych do przetwarzania plików tekstowych podczas przetwarzania prawdopodobnie plików binarnych, ale tutaj będzie działać „tail + 32c”.
jlliagre
1
@jlliagre: Napisałeś cut (czy to nie powinien być ogon? ... asis, to nie działa dla mnie ...
Peter.O
Oczywiście, to ogon. Przepraszam za niedopasowanie.
jlliagre
@jlliagre: W Solarisie powinieneś /usr/xpg4/binwyprzedzić /usr/binswoją działalność PATH, inaczej utkniesz na początku lat 90. Wiele unikatów (np. GNU, BusyBox) nie obsługuje już +32cskładni historycznej i oznacza to plik o nazwie +32c(jak wymaga POSIX).
Gilles „SO- przestań być zły”
12

Następujące polecenia wycinają pierwsze 31 bajtów $file(używając $file~jako kopii tymczasowej):

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

Musisz tylko wyświetlić listę lub findwszystkie pliki poniżej /foo/i wykonać dwa powyższe dla każdego $fileznalezionego pliku .

alex
źródło
1
Zamiana wartości bs i skip zwiększy wydajność.
jlliagre
10

tail -c +32wyprowadza dane wejściowe minus pierwsze 31 bajtów. (Tak, argument jest wyłączony o jeden). Aby edytować plik na miejscu, użyj gąbki w pętli, a jeśli go nie masz i nie chcesz się tym przejmować, wykonaj swoją pracę w powłoce:

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

Jeśli polecenia zostaną przerwane z jakiegokolwiek powodu (np. Awaria zasilania), może być trudno ustalić, gdzie przerwałeś. Zapisanie nowych plików w osobnym katalogu ułatwiłoby to.

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

Jeśli pliki są naprawdę duże (na tyle duże, że problem stanowią dwie kopie nawet jednego), możesz skorzystać z jednej z technik wymienionych w tym wątku .

Gilles „SO- przestań być zły”
źródło
2

Możesz używać Vima w trybie Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % wybierz wszystkie linie

  2. ! Uruchom polecenie

  3. x Zapisz i zamknij

Steven Penny
źródło