Elimina i primi n byte di file


32

Ho un problema estremo e tutte le soluzioni che posso immaginare sono complicate. Secondo la mia esperienza UNIX / Linux ci deve essere un modo semplice.

Voglio eliminare i primi 31 byte di ciascun file in /foo/. Ogni file è abbastanza lungo. Bene, sono sicuro che qualcuno mi offrirà una soluzione sorprendentemente facile che non riesco proprio a immaginare. Forse awk?


2
Qualsiasi soluzione awk / sed / ed sarà orientata alla linea, quindi se non si conosce la prima riga avrà almeno 31 caratteri, ne conseguiranno complicazioni.
Glenn Jackman,

Risposte:


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

o il più veloce, grazie al suggerimento di Gilles:

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

Nota: la coda Posix specifica "-c +32" invece di "+ 32c" ma alla coda di default di Solaris non piace:

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

/usr/xpg4/bin/tail va bene con entrambe le sintassi.


1
Suggerire ddqui è eccessivo, tailè più appropriato (più semplice, meno rischi di errori di battitura, nessun messaggio spurio su Stderr).
Gilles 'SO- smetti di essere malvagio'

Hai ragione. Di solito evito i comandi destinati a elaborare i file di testo durante l'elaborazione di quelli eventualmente binari ma "tail + 32c" funzionerà qui.
jlliagre,

1
@jlliagre: hai scritto cut (non dovrebbe essere coda? ... asis, non funziona per me ...
Peter.O

Certo, è la coda. Mi dispiace per la mancata corrispondenza.
jlliagre,

@jlliagre: su Solaris, dovresti avere un /usr/xpg4/binvantaggio /usr/binsul tuo PATH, o rimarrai bloccato nei primi anni '90. Molti unices (ad esempio GNU, BusyBox) non supportano più la +32csintassi storica e considerano un file chiamato +32c(come richiede POSIX).
Gilles 'SO- smetti di essere malvagio'

12

I seguenti comandi tagliano i primi 31 byte da $file(usando $file~come copia temporanea):

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

Hai solo bisogno di elencare o findtutti i file in /foo/ed eseguire i due sopra per ogni $filetrovato.


1
Scambiare bs e saltare i valori aumenterà le prestazioni.
jlliagre,

10

tail -c +32genera il suo input meno i primi 31 byte. (Sì, l'argomento è disattivato da uno.) Per modificare un file sul posto, usa sponge in un ciclo, o se non lo hai e non vuoi disturbarti, fai il suo lavoro nella 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 i comandi vengono interrotti per qualsiasi motivo (ad es. Un'interruzione di corrente), potrebbe essere difficile capire da dove eri rimasto. Scrivere i nuovi file in una directory separata semplificherebbe le cose.

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 i file sono molto grandi (come in, abbastanza grandi da avere due copie di uno solo, è un problema), puoi usare una delle tecniche menzionate in questo thread .


2

Puoi usare Vim in modalità Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % seleziona tutte le righe

  2. ! esegui comando

  3. x salva e chiudi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.