Posso fare in modo che `cut` cambi un file sul posto?


Risposte:


14

Non puoi. Usa ed o GNU sed o perl, oppure fai quello che fanno dietro le quinte, ovvero creare un nuovo file per i contenuti.

ed, portatile:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, creando un nuovo file (consigliato, perché se lo script viene interrotto, puoi semplicemente eseguirlo di nuovo):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, sostituendo il file in atto (mantiene la proprietà e le autorizzazioni di foo, ma necessita di protezione contro le interruzioni):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Consiglio di utilizzare uno dei cutmetodi basati su. In questo modo non dipendi da nessuno strumento non standard, puoi utilizzare lo strumento migliore per il lavoro e controllare il comportamento in caso di interruzione.


Meglio del .oldmetodo per le modifiche sul posto,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut il

1
@GypsyCosmonaut No, questo non è "migliore". È più fragile. Il suo unico vantaggio è che è più breve da digitare. Il problema principale con il metodo è che se si verifica un errore durante l'elaborazione del file di input o la scrittura dell'output, i dati vengono persi. Inoltre non funziona con i dati binari: l'output verrà troncato al primo byte null. Anche con file di testo, rimuove le righe vuote dalla fine del file. Con file di grandi dimensioni, potrebbe non riuscire perché i dati devono essere archiviati come stringa nella memoria della shell (e ricordare, se ciò accade, i dati vengono persi).
Gilles 'SO- smetti di essere malvagio' il

oO Grazie, non sapevo che potessero esserci problemi con byte nulli
GypsyCosmonaut,

Penso che sia più semplice:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh

@LoMaPh In effetti, non so perché ho rinominato il vecchio file: non ha alcun vantaggio rispetto alla ridenominazione del nuovo file. È anche più semplice perché non è necessario il passaggio rm foo. E non dovresti chiamare rm foo, perché mv foo.new fooè atomico: rimuove la vecchia versione e mette in atto la nuova versione allo stesso tempo.
Gilles 'SO- smetti di essere malvagio' il

23

Il pacchetto moreutils di Ubuntu (e anche di Debian ) ha un programma chiamato sponge, che risolve anche il tuo problema.

Da uomo spugna:

sponge legge l'input standard e lo scrive nel file specificato. A differenza di un reindirizzamento della shell, la spugna assorbe tutto il suo input prima di aprire il file di output. Ciò consente di costringere pipeline che leggono e scrivono nello stesso file.

Che ti permetterebbe di fare qualcosa del genere:

cut -d <delim> -f <fields> somefile | sponge somefile

9

Non penso che sia possibile usare cutda soli. Non sono riuscito a trovarlo nella pagina man o info. Puoi fare qualcosa come

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempti rende un file temporaneo relativamente sicuro in cui puoi convogliare l' cutoutput.


1

Prova vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Ciò modificherà il file sul posto (quindi esegui prima il backup).

In alternativa grep, utilizzare , sedo gawk.


0

Puoi usare slurp con POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Esempio


0

Bene, poiché cutproduce meno output di quello che legge, puoi fare:

cut -c1 < file 1<> file

Vale a dire, rendere il suo stdin fileaperto in modalità sola lettura e il suo stdout fileaperto in modalità lettura + scrittura senza troncamento ( <>).

In questo modo, cutsovrascriverà il file su se stesso. Tuttavia, il resto del file non verrà toccato. Ad esempio, se filecontiene:

foo
bar

L'output diventerà:

f
b
bar

L' f\nb\nhanno sostituito foo\n, ma barè ancora lì. Dovresti troncare il file al cuttermine.

Con ksh93, puoi farlo con il suo <>;operatore che agisce come <>se non fosse che se il comando ha successo, ftruncate()viene chiamato sul descrittore di file. Così:

cut -c1 < file 1<>; file

Con altre shell, avresti bisogno di farlo ftruncate()tramite altri mezzi come:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

anche se invocare perlsolo per questo è un po 'eccessivo qui soprattutto considerando che perlpuò facilmente fare quel cutlavoro come:

perl -pi -e '$_ = substr($_, 0, 1)' file

Fai attenzione che con tutti i metodi che prevedono l'effettiva riscrittura sul posto, se l'operazione viene interrotta a metà strada, finirai con un file danneggiato. L'uso di un secondo file temporaneo evita questo problema.

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.