Exclua os primeiros n bytes de arquivos


32

Eu tenho um problema extremo e todas as soluções que posso imaginar são complicadas. De acordo com minha experiência em UNIX / Linux, deve haver uma maneira fácil.

Quero excluir os primeiros 31 bytes de cada arquivo /foo/. Cada arquivo é longo o suficiente. Bem, tenho certeza de que alguém me fornecerá uma solução surpreendentemente fácil que simplesmente não consigo imaginar. Talvez awk?


2
Qualquer solução awk / sed / ed será orientada a linhas; portanto, se você não souber, a primeira linha terá pelo menos 31 caracteres e ocorrerão complicações.
Glenn Jackman

Respostas:


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

ou mais rápido, graças à sugestão de Gilles:

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

Nota: A cauda do Posix especifica "-c +32" em vez de "+ 32c", mas a cauda padrão do Solaris não gosta:

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

/usr/xpg4/bin/tail está bem com ambas as sintaxes.


1
Sugerir ddaqui é um exagero, tailé mais apropriado (mais simples, menos risco de erro de digitação, nenhuma mensagem espúria no stderr).
Gilles 'SO- stop be evil'

Você está certo. Normalmente evito comandos destinados a processar arquivos de texto ao processar possivelmente binários, mas "tail + 32c" funcionará aqui.
Jlliagre 28/05

1
@jlliagre: Você escreveu cut ? (não deve a cauda ser ... Asis, ele não funciona para mim ...
Peter.O

Claro, é cauda. Desculpe pela incompatibilidade.
Jlliagre 28/05

@jlliagre: No Solaris, você deveria ter /usr/xpg4/binantecipado o /usr/binseu PATH, ou ficará preso no início dos anos 90. Muitos departamentos (por exemplo, GNU, BusyBox) não suportam mais a +32csintaxe histórica e consideram um arquivo chamado +32c(conforme requer o POSIX).
Gilles 'SO- stop be evil'

12

Os seguintes comandos cortam os primeiros 31 bytes $file(usando $file~como uma cópia temporária):

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

Você só precisa listar ou findtodos os arquivos abaixo /foo/e executar os dois acima para cada $fileencontrado.


1
Trocar valores bs e pular aumentará o desempenho.
Jlliagre 27/05

10

tail -c +32gera sua entrada menos os primeiros 31 bytes. (Sim, o argumento está desativado em um.) Para editar um arquivo no local, use esponja em um loop ou, se você não o tiver e não quiser se preocupar, faça seu trabalho no shell:

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

Se os comandos forem interrompidos por qualquer motivo (por exemplo, falta de energia), pode ser difícil descobrir de onde você parou. Gravar os novos arquivos em um diretório separado facilitaria as coisas.

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

Se os arquivos forem realmente grandes (por exemplo, grandes o suficiente para que duas cópias de uma única sejam um problema), você poderá usar uma das técnicas mencionadas neste encadeamento .


2

Você pode usar o Vim no modo Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % selecione todas as linhas

  2. ! comando de execução

  3. x salvar e fechar

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.