Comando Unix per anteporre il testo a un file


126

Esiste un comando Unix per anteporre alcuni dati di stringa a un file di testo?

Qualcosa di simile a:

prepend "to be prepended" text.txt

stai cercando un singolo comando o un piccolo comando script / alias va bene?
Levon

2
Se combini tutte le risposte, questo è probabilmente il modo più compatto:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat

Questa domanda ha davvero fatto emergere la creatività in molte persone!
Richard Jessop

Risposte:


111
sed -i.old '1s;^;to be prepended;' inFile
  • -iscrive la modifica in posizione ed esegue un backup se viene fornita un'estensione. (In questo caso, .old)
  • 1s;^;to be prepended;sostituisce l'inizio della prima riga con la stringa di sostituzione data, utilizzando ;come delimitatore di comando.

19
Se potessi aggiungere qualche spiegazione con il comando, sarebbe utile per altri che non hanno familiarità con il funzionamento di sed. OP potrebbe voler inserire a \ndopo anteporre; a seconda delle loro esigenze. Ottima soluzione.
Levon

Sì, una spiegazione sarebbe fantastica. Possono le inFiledirectory di inclusione nel suo percorso?
zakdances

3
@ Levon, volevo assolutamente inserire un file \n. Avrei dovuto leggere prima i commenti. Dannazione.
chishaku

Nel mio caso voglio avere un punto e virgola alla fine della riga anteposta, quindi sono andato con'1s;^;my-prepended-line\;\n;'
SamGamgee

5
Nota che '\ n' funziona solo per GNU sed, non BSD (come OSX, FreeBSD, ecc.). Per essere più portatile con quelli, vedi: stackoverflow.com/questions/1421478/...
JWD

164
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

2
In una riga e nel file di testo originale! Questa è la semplice risposta corretta. Suggerisco che la nuova riga aggiuntiva \ n echo -e "sia anteposta \ n $ (cat text.txt)"> text.txt
VincentPerrin.com

32
Questa soluzione ha dei problemi ... converte le occorrenze di "\ n" in nuove righe effettive ... problematico se stai anteponendo a un file di codice con stringhe che contengono "\ n".
Paul Go

1
Avevo questo nel mio file git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';e questo incasina mentre converte i file \te \n.
hIpPy

8
Questo (1) carica l'intero file in memoria e (2) inserisce l'intero file in un singolo argomento della riga di comando. Va bene per cose semplici, ma attenzione ai limiti.
jwd

2
L'ultima volta che ho provato, credo che ciò si interromperebbe su file molto grandi, in cui cat non riesce a leggere l'intero contenuto di text.txt prima che venga sovrascritto. Se l'opzione -e su echo è un problema, puoi citare una nuova riga in bash o fareecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112

44

Sostituzione del processo

Sono sorpreso che nessuno ne abbia parlato.

cat <(echo "before") text.txt > newfile.txt

che è probabilmente più naturale della risposta accettata (stampare qualcosa e collegarlo a un comando di sostituzione è lessicograficamente controintuitivo).

... e dirottando ciò che ha detto ryan sopra, con spongenon hai bisogno di un file temporaneo:

sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt

EDIT: Sembra che questo non funzioni in Bourne Shell /bin/sh


Here String (solo zsh)

Usando una stringa qui - <<<, puoi fare:

<<< "to be prepended" < text.txt | sponge text.txt

3
Questa risposta è il vincitore per me. Semplice e realizzabile solo con incorporati. Buon lavoro!
PiersyP

1
@PiersyP sono d'accordo! Questo ha aiutato molto, grazie Sridhar-Sarnobat.

Il problema è che c'è 1 file, non 2 file. Quindi la prima riga della risposta non funziona davvero. L'ultima riga potrebbe funzionare (ma richiede una spugna).
VasiliNovikov

1
Cosa fa la spugna?
Hemanta

1
Le tue costruzioni <<(echo "to be prepended") < text.txte <<< "to be prepended" < text.txtnon funzionano in bash; richiedono zsh.
Anders Kaseorg

32

Questa è una possibilità:

(echo "to be prepended"; cat text.txt) > newfile.txt

probabilmente non potrai aggirare facilmente un file intermedio.

Alternative (può essere complicato con la fuga dalla shell):

sed -i '0,/^/s//to be prepended/' text.txt

La strategia 1) è la più intuitiva di tutte le risposte qui, credo. E una piccola variazione: cat <(echo "to be prepended") text.txt > newfile.txt . A pensarci bene, non sono sicuro che il mio sia correlato, quindi sto postando una risposta separata.
Sridhar Sarnobat

1
Il primo metodo non manterrà i permessi dei file
Xlsx

L'unico modo per preservare i permessi è usare la spugna
Sridhar Sarnobat il

20

Questo funzionerà per formare l'output. - indica lo standard input, che viene fornito tramite la pipe da echo.

echo -e "to be prepended \n another line" | cat - text.txt

Per riscrivere il file è necessario un file temporaneo in quanto non può essere reindirizzato nel file di input.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt

2
questo non antepone il testo al file text.txtcome richiesto, ma lo visualizza su stdout. L'output potrebbe essere incanalato in un file diverso se funziona per l'OP
Levon

1
questo indicherebbe sicuramente "da anteporre" e quindi il contenuto di "text.txt". Ma vorrei che il testo fosse anteposto al file
One Two Three

1
e se ho testo su più righe? Come inserisco il carattere di nuova riga?
One Two Three

Sarebbe bello ma a bash non piace: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: il file di input è un file di output
geek-merlin

14

Preferisco la risposta di Adam

Possiamo rendere più facile l'uso della spugna . Ora non è necessario creare un file temporaneo e rinominarlo con

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt

questa era l'unica soluzione che funzionava per me. è abbastanza divertente che il nome del mio server sia spongebob.
Ömer An

11

Se è accettabile sostituire il file di input:

Nota:

  • Ciò potrebbe avere effetti collaterali inaspettati, in particolare la potenziale sostituzione di un collegamento simbolico con un file normale, la conclusione con autorizzazioni diverse sul file e la modifica della data di creazione (nascita) del file.

  • sed -i, come nella risposta del principe John Wesley , cerca di ripristinare almeno i permessi originali, ma si applicano anche le altre limitazioni.

Ecco una semplice alternativa che utilizza un file temporaneo (evita di leggere l'intero file di input in memoria come fa la soluzione di shime ):

{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt

L'utilizzo di un comando group ( { ...; ...; }) è leggermente più efficiente rispetto all'utilizzo di una subshell ( (...; ...)), come nella soluzione di 0xC0000022L .

I vantaggi sono:

  • E 'facile da controllare se il nuovo testo dovrebbe essere direttamente preposta alla prima linea o se debba essere inserito come nuova linea (s) (semplicemente accodamento \nal printfargomento).

  • A differenza della sedsoluzione, funziona se il file di input è vuoto ( 0byte).


La sedsoluzione può essere semplificata se l'intento è di anteporre una o più righe intere al contenuto esistente (supponendo che il file di input non sia vuoto):

sedLa ifunzione di inserisce intere righe:

Con GNU sed :

# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile

Una variante portatile che funziona anche con macOS / BSD sed:

# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile

Nota che la nuova riga letterale dopo \è obbligatoria.


Se il file di input deve essere modificato in posizione (preservando il suo inode con tutti i suoi attributi):

Utilizzando la venerabile edutility POSIX :

Nota:

  • edlegge invariabilmente prima il file di input nel suo complesso in memoria.

Per anteporre direttamente alla prima riga (come con sed, questo non funzionerà se il file di input è completamente vuoto ( 0byte)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedmessaggi di stato soppressi .
  • Nota come i comandi vengono forniti edcome here-document ( <<EOF\n...\nEOF) su più righe , cioè tramite stdin ; per impostazione predefinita, l'espansione delle stringhe viene eseguita in tali documenti (le variabili di shell sono interpolate); cita il delimitatore di apertura per sopprimerlo (ad esempio <<'EOF').
  • 1 rende la prima riga la riga corrente
  • la funzione sesegue una sostituzione di stringa basata su espressioni regolari sulla riga corrente, come in sed; puoi includere caratteri di ritorno a capo letterali nel testo sostitutivo, ma devono essere \preceduti da caratteri di escape.
  • wriscrive il risultato nel file di input (per il test, sostituire wcon ,pper stampare solo il risultato, senza modificare il file di input).

Per anteporre una o più righe intere :

Come con sed, la ifunzione aggiunge invariabilmente una nuova riga finale al testo da inserire.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 irende 0(l'inizio del file) la riga corrente e avvia la modalità di inserimento ( i); notare che i numeri di riga sono altrimenti 1basati su.
  • Le righe seguenti sono il testo da inserire prima della riga corrente, terminata con .su una riga separata.

7

Probabilmente nulla di integrato, ma potresti scrivere il tuo abbastanza facilmente, in questo modo:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

Almeno qualcosa del genere ...


6
+1 per l'utilizzo di $$ (il PID corrente) come parte del nome del file temporaneo. Se si crea uno script per uso generale, questo impedirà a un altro utente di sovrascrivere potenzialmente il file temporaneo eseguendo lo stesso script contemporaneamente.
E Brown

3
$$Tuttavia, l' utilizzo non è sufficiente per il codice di produzione (attacco tramite collegamento simbolico di Google); ma è sicuramente meglio di un nome di file statico.
tripleee

1
basta usaremktemp
mewa

7

In alcune circostanze il testo anteposto può essere disponibile solo da stdin. Allora questa combinazione funzionerà.

echo "to be prepended" | cat - text.txt | tee text.txt

Se vuoi omettere l' teeoutput, aggiungi > /dev/null.


Mi piace molto questa risposta. È un modo molto pulito e chiaro per farlo. L'uso di tee è una soluzione naturale al problema di provare a catare l'output nel file di input. Inoltre nessun hack con la ridenominazione. Meglio della soluzione più votata in questo momento, poiché non crea un nuovo file. Questo è fondamentalmente il più vicino possibile a >> per anteporre invece di aggiungere.
DC2

Questa è di gran lunga la risposta migliore e più pulita.
nerdoc

6
C'è una garanzia che teenon cada text.txtprima catdi averlo letto? Penso di no, il che renderebbe questa soluzione pericolosa per la salute del file.
Jonathan Leffler

3
@JonathanLeffler probabilmente no. Ho provato questo codice di prova: lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null. Se lenAè 1000000 (file da 1 lenBMB ) ed è 10000 (anteprime di testo di 10 KB), il file "a.txt" viene sovrascritto con 20 KB di lettere "b". Questo è totalmente rotto. Ora, se usi 1Mb a.txt e 1Mb di testo per anteporre, teeentra in un ciclo che genera file 7Gb +, ho dovuto interrompere il comando. Quindi, è ovvio che il risultato è imprevedibile per i grandi formati. Non ho informazioni se dovrebbe funzionare su piccole dimensioni.
VasiliNovikov

3
Per dirla in termini concettuali: questo comando provocherà la perdita di dati se il file di input è più grande della dimensione del buffer della pipeline del sistema , che oggigiorno è tipicamente 64 KB.
mklement0

7

Soluzione:

printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt

Nota che questo è sicuro su tutti i tipi di input, perché non ci sono espansioni. Ad esempio, se vuoi anteporre !@#$%^&*()ugly text\n\t\n, funzionerà semplicemente:

printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt

L'ultima parte da considerare è la rimozione degli spazi bianchi alla fine del file durante la sostituzione del comando "$(cat file.txt)". Tutte le soluzioni per questo sono relativamente complesse. Se desideri conservare le nuove righe alla fine di file.txt, consulta questo: https://stackoverflow.com/a/22607352/1091436


Lo adoro. Super portatile e non necessita di generare un nuovo file. Grazie!
pyb

1
L'unico problema con questo si verifica se il contenuto di file.txtè più grande di quello che può essere adattato nell'elenco degli argomenti a printf.
Jonathan Leffler

6

Un altro modo di utilizzare sed:

sed -i.old '1 {i to be prepended
}' inFile

Se la riga da anteporre è multilinea:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile

Perché no sed -i '1ito be prepended' inFile- o è consentito solo per GNU sed?
utente sconosciuto

@userunknown: in standard sed, il icomando è seguito da una barra rovesciata e da una nuova riga, e ogni riga di input tranne l'ultima termina anche con una barra rovesciata. GNU sedconsente abbreviazioni sulla falsariga di cui chiedi, ma è solo GNU a sedfarlo. '
Jonathan Leffler

3

Come testato in Bash (in Ubuntu), se si inizia con un file di prova tramite;

echo "Original Line" > test_file.txt

puoi eseguire;

echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt

oppure, se la versione di bash è troppo vecchia per $ (), puoi usare i backtick;

echo "`echo "New Line"; cat test_file.txt`" > test_file.txt

e ricevere il seguente contenuto di "test_file.txt";

New Line
Original Line

Nessun file intermedio, solo bash / echo.


2
migliore risposta, ho scritto questo one-liner per ruotare il mio file usando questa risposta: for i in $ (<file2.txt); fai echo "$ (echo $ i; cat file.txt)"> file.txt; fatto;
Aurangzeb

2

Un'altra soluzione abbastanza semplice è:

    $ echo -e "string\n" $(cat file)

Questo ha funzionato per me ... solo una leggera modifica, per mantenere le multilinee dal file di input devi racchiuderlo tra virgolette quindi qualcosa del genere: echo -e "stringa \ n" "$ (cat ~ / file.txt) "> ~ / file.txt;
Craig Wayne

2
Non avere l' catespansione dei parametri tra virgolette è una buona ragione per un voto negativo . Questo codice divide il contenuto del file in parole e passa ciascuna di quelle parole come argomento separato a echo. Si perdono i confini dell'argomento originale, si perdono le nuove righe / tabulazioni / ecc. E le stringhe come \te \nnel testo originale vengono sostituite con tabulazioni e nuove righe invece di essere mantenute com'erano.
Charles Duffy

1

Se ti piace vi / vim, questo potrebbe essere più il tuo stile.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

0
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo

0

Consiglierei di definire una funzione e quindi di importarla e utilizzarla dove necessario.

prepend_to_file() { 
    file=$1
    text=$2

    if ! [[ -f $file ]] then
        touch $file
    fi

    echo "$text" | cat - $file > $file.new

    mv -f $file.new $file
}

Quindi usalo in questo modo:

prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"

Il contenuto del file sarà quindi:

This is second
This is first

Sto per utilizzare questo approccio per implementare un programma di aggiornamento del registro delle modifiche.

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.