Come posso eseguire qualsiasi comando modificando il suo file (argomento) & # 8220; in posizione & # 8221; usando bash?
-
02-07-2019 - |
Domanda
Ho un file temp.txt, che voglio ordinare con il comando sort
in bash.
Voglio che i risultati ordinati sostituiscano il file originale.
Questo non funziona ad esempio (ottengo un file vuoto):
sortx temp.txt > temp.txt
È possibile farlo in una riga senza ricorrere alla copia in file temporanei?
EDIT: l'opzione -o
è molto interessante per sort
. Ho usato sort
nella mia domanda come esempio. Ho riscontrato lo stesso problema con altri comandi:
uniq temp.txt > temp.txt.
Esiste una soluzione generale migliore?
Soluzione
sort temp.txt -o temp.txt
Altri suggerimenti
Un sort
deve vedere tutti gli input prima di poter iniziare a produrre. Per questo motivo, il programma sort
può facilmente offrire un'opzione per modificare un file sul posto:
sort temp.txt -o temp.txt
In particolare, la della documentazione di GNU sort
dice:
Normalmente, l'ordinamento legge tutti gli input prima di aprire il file di output, quindi puoi ordinare un file in sicurezza usando comandi come
sort -o F F
ecat F | ordina -o F
. Tuttavia,sort
con--merge
(-m
) può aprire il file di output prima di leggere tutti gli input, quindi un comando comecat F | sort -m -o F - G
non è sicuro in quanto l'ordinamento potrebbe iniziare a scrivereF
prima checat
abbia finito di leggerlo.
Mentre la documentazione di BSD sort
dice:
Se [il] file di output è uno dei file di input, l'ordinamento lo copia in un file temporaneo prima di ordinare e scrivere l'output nel [file] output.
Comandi come uniq
possono iniziare a scrivere output prima che finiscano di leggere l'input. Questi comandi in genere non supportano la modifica sul posto (e sarebbe più difficile per loro supportare questa funzione).
In genere si risolve questo problema con un file temporaneo o se si desidera assolutamente evitare di disporre di un file intermedio, è possibile utilizzare un buffer per memorizzare il risultato completo prima di scriverlo. Ad esempio, con perl
:
uniq temp.txt | perl -e 'undef $/; Un sort
deve vedere tutti gli input prima di poter iniziare a produrre. Per questo motivo, il programma sort
può facilmente offrire un'opzione per modificare un file sul posto:
sort temp.txt -o temp.txt
In particolare, la della documentazione di GNU sort
dice:
Normalmente, l'ordinamento legge tutti gli input prima di aprire il file di output, quindi puoi ordinare un file in sicurezza usando comandi come sort -o F F
e cat F | ordina -o F
. Tuttavia, sort
con --merge
( -m
) può aprire il file di output prima di leggere tutti gli input, quindi un comando come cat F | sort -m -o F - G
non è sicuro in quanto l'ordinamento potrebbe iniziare a scrivere F
prima che cat
abbia finito di leggerlo.
Mentre la documentazione di BSD sort
dice:
Se [il] file di output è uno dei file di input, l'ordinamento lo copia in un file temporaneo prima di ordinare e scrivere l'output nel [file] output.
Comandi come uniq
possono iniziare a scrivere output prima che finiscano di leggere l'input. Questi comandi in genere non supportano la modifica sul posto (e sarebbe più difficile per loro supportare questa funzione).
In genere si risolve questo problema con un file temporaneo o se si desidera assolutamente evitare di disporre di un file intermedio, è possibile utilizzare un buffer per memorizzare il risultato completo prima di scriverlo. Ad esempio, con perl
:
<*>
Qui, la parte perl legge l'output completo da uniq
nella variabile $ _
e quindi sovrascrive il file originale con questi dati. Potresti fare lo stesso nel linguaggio di script che preferisci, forse anche in Bash. Tuttavia, sarà necessario memoria sufficiente per archiviare l'intero file, questo non è consigliabile quando si lavora con file di grandi dimensioni.
= <>; open(OUT,">temp.txt"); print OUT;'
Qui, la parte perl legge l'output completo da uniq
nella variabile $ _
e quindi sovrascrive il file originale con questi dati. Potresti fare lo stesso nel linguaggio di script che preferisci, forse anche in Bash. Tuttavia, sarà necessario memoria sufficiente per archiviare l'intero file, questo non è consigliabile quando si lavora con file di grandi dimensioni.
Ecco un approccio più generale, funziona con uniq, sort e whatnot.
{ rm file && uniq > file; } < file
Il commento di Tobu sulla spugna garantisce di essere una risposta a sé stante.
Per citare dalla moreutils homepage:
Probabilmente lo strumento più generico finora in moreutils è sponge (1), che ti permette di fare cose del genere:
% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd
Tuttavia, sponge
soffre dello stesso problema Steve Steveop commenta qui. Se presente dei comandi nella pipeline prima che sponge
fallisca, il file originale verrà sovrascritto.
$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found
Uh-oh, il mio file importante
non c'è più.
Ecco una riga:
sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt
Tecnicamente non è possibile copiare in un file temporaneo e il comando 'mv' dovrebbe essere istantaneo.
Mi piace la risposta ordina file -o file
ma non voglio digitare due volte lo stesso nome file.
Uso di BASH espansione della cronologia :
$ sort file -o !#^
prende il primo argomento della riga corrente quando si preme invio .
Un ordinamento unico sul posto:
$ sort -u -o file !#$
prende l'ultimo arg nella riga corrente.
Molti hanno menzionato l'opzione -o . Ecco la parte della pagina man.
Dalla pagina man:
-o output-file
Write output to output-file instead of to the standard output.
If output-file is one of the input files, sort copies it to a
temporary file before sorting and writing the output to output-
file.
Questo sarebbe fortemente limitato dalla memoria, ma potresti usare awk per memorizzare i dati intermedi in memoria, e poi riscriverli.
uniq temp.txt | awk '{line[i++] = <*>}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
Un'alternativa a spugna
con il sed
più comune:
sed -ni r<(command file) file
Funziona con qualsiasi comando ( sort
, uniq
, tac
, ...) e utilizza il ben noto sed
-i
> (modifica i file sul posto).
Avviso: prova prima file di comando
perché la modifica dei file sul posto non è sicura per natura.
Spiegazione
In primo luogo, stai dicendo a sed
di non stampare le righe (originali) ( -n
opzione ) e con l'aiuto di r
comando e bash
's Sostituzione processo , il contenuto generato da < (file di comando)
sarà l'output salvato sul posto .
Semplifica le cose
Puoi avvolgere questa soluzione in una funzione:
ip_cmd() { # in place command
CMD=${1:?You must specify a command}
FILE=${2:?You must specify a file}
sed -ni r<("$CMD" "$FILE") "$FILE"
}
Esempio
$ cat file
d
b
c
b
a
$ ip_cmd sort file
$ cat file
a
b
b
c
d
$ ip_cmd uniq file
$ cat file
a
b
c
d
$ ip_cmd tac file
$ cat file
d
c
b
a
$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
Usa l'argomento --output =
o -o
Ho appena provato su FreeBSD:
sort temp.txt -otemp.txt
Per aggiungere la funzionalità uniq
, quali sono gli svantaggi di:
sort inputfile | uniq | sort -o inputfile
Leggi sull'editor non interattivo, ex
.
Se insisti nell'usare il programma sort
, devi usare un file intermedio - non credo che sort
abbia un'opzione per l'ordinamento in memoria. Qualsiasi altro trucco con stdin / stdout fallirà a meno che tu non possa garantire che la dimensione del buffer per lo stdin di sort sia abbastanza grande da adattarsi all'intero file.
Modifica: vergogna per me. sort temp.txt -o temp.txt
funziona in modo eccellente.
Un'altra soluzione:
uniq file 1<> file