Commande Unix pour ajouter du texte à un fichier

126

Existe-t-il une commande Unix pour ajouter des chaînes de caractères dans un fichier texte?

Quelque chose comme:

prepend "to be prepended" text.txt
Un deux trois
la source
recherchez-vous une seule commande ou une petite commande de script / alias est-elle correcte?
Levon
2
Si vous combinez toutes les réponses, c'est probablement le moyen le plus compact:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat
Cette question a vraiment fait ressortir la créativité de beaucoup de gens!
Richard Jessop

Réponses:

111
sed -i.old '1s;^;to be prepended;' inFile
  • -iécrit le changement en place et effectue une sauvegarde si une extension est donnée. (Dans ce cas, .old)
  • 1s;^;to be prepended;remplace le début de la première ligne par la chaîne de remplacement donnée, en utilisant ;comme délimiteur de commande.
Prince John Wesley
la source
19
Si vous pouviez ajouter des explications avec la commande, cela serait utile pour les autres qui ne sont pas si familiers avec le fonctionnement de sed. OP peut vouloir insérer \naprès le préfixe; en fonction de leurs besoins. Solution soignée.
Levon
Ouais, une explication serait géniale. inFileLes répertoires inclus dans son chemin peuvent-ils être inclus?
zakdances
3
@Levon Je voulais absolument insérer un fichier \n. J'aurais dû lire les commentaires en premier. Zut.
chishaku
Dans mon cas, je veux avoir un point-virgule à la fin de la ligne préfixée, alors je suis allé avec'1s;^;my-prepended-line\;\n;'
SamGamgee
5
Notez que '\ n' ne fonctionne que pour GNU sed, pas BSD (comme OSX, FreeBSD, etc.). Pour être plus portable avec ceux-ci, voir: stackoverflow.com/questions/1421478/…
jwd
164
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
shime
la source
2
En une seule ligne et dans le fichier texte d'origine! C'est la bonne réponse simple. Je suggère que la nouvelle ligne \ n echo -e "soit
VincentPerrin.com
32
Cette solution pose des problèmes ... elle convertit les occurrences de "\ n" en nouvelles lignes réelles ... problématique si vous ajoutez un fichier de code avec des chaînes contenant "\ n".
Paul Go
1
J'avais ceci dans mon dossier git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';et cela gâche car il convertit le \tet \n.
hIpPy
8
Ceci (1) charge le fichier entier en mémoire, et (2) colle le fichier entier dans un seul argument de ligne de commande. Bien pour les choses simples, mais méfiez-vous des limitations.
jwd
2
La dernière fois que j'ai essayé, je pense que cela se briserait sur des fichiers très volumineux, où cat ne parvient pas à lire tout le contenu de text.txt avant qu'il ne soit écrasé. Si l'option -e sur l'écho pose un problème, vous pouvez citer une nouvelle ligne dans bash ou doecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112
44

Substitution de processus

Je suis surpris que personne n'ait mentionné cela.

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

ce qui est sans doute plus naturel que la réponse acceptée (imprimer quelque chose et le faire passer dans une commande de substitution est contre-intuitif lexicographiquement).

... et détournant ce que Ryan a dit ci-dessus, spongevous n'avez pas besoin d'un fichier temporaire:

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

EDIT: On dirait que cela ne fonctionne pas dans Bourne Shell /bin/sh


Here String (zsh uniquement)

En utilisant une chaîne ici - <<<, vous pouvez faire:

<<< "to be prepended" < text.txt | sponge text.txt
Sridhar Sarnobat
la source
3
Cette réponse est la gagnante pour moi. Simple et réalisable avec les fonctions intégrées uniquement. Bon travail!
PiersyP
1
@PiersyP Je suis d'accord! Cela a beaucoup aidé, merci Sridhar-Sarnobat.
Le problème est qu'il y a 1 fichier, pas 2 fichiers. La première ligne de réponse ne fonctionne donc pas vraiment. La dernière ligne peut fonctionner (mais elle nécessite une éponge).
VasiliNovikov
1
Que fait l'éponge?
Hemanta le
1
Votre <<(echo "to be prepended") < text.txtet vos <<< "to be prepended" < text.txtconstructions ne fonctionnent pas en bash; ils nécessitent zsh.
Anders Kaseorg
32

Voici une possibilité:

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

vous ne pourrez probablement pas contourner facilement un fichier intermédiaire.

Alternatives (peuvent être lourdes avec l'échappement du shell):

sed -i '0,/^/s//to be prepended/' text.txt
0xC0000022L
la source
La stratégie 1) est la plus intuitive de toutes les réponses ici, je pense. Et une légère variation: cat <(echo "to be prepended") text.txt > newfile.txt . À bien y penser, je ne suis pas sûr que le mien soit lié, alors je poste une réponse séparée.
Sridhar Sarnobat
1
La première méthode ne conservera pas les autorisations de fichier
Xlsx
Le seul moyen de préserver les autorisations est d'utiliser l'éponge
Sridhar Sarnobat
20

Cela fonctionnera pour former la sortie. Le - signifie une entrée standard, qui est fournie via le tuyau de l'écho.

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

Pour réécrire le fichier, un fichier temporaire est requis car il ne peut pas être redirigé vers le fichier d'entrée.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
Adam
la source
2
cela n'ajoute pas le texte au fichier text.txtcomme demandé, mais l'affiche sur stdout. La sortie pourrait être canalisée dans un fichier différent si cela fonctionne pour l'OP
Levon
1
cela afficherait définitivement le "à être ajouté" puis le contenu de "text.txt". Mais j'aimerais que le texte soit ajouté au dossier
One Two Three
1
et que faire si j'ai du texte sur plusieurs lignes? Comment insérer le caractère de nouvelle ligne ici?
One Two Three
Ce serait cool mais bash n'aime pas ça: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: le fichier d'entrée est le fichier de sortie
geek-merlin
14

Préfère la réponse d'Adam

Nous pouvons faciliter l'utilisation de l' éponge . Maintenant, nous n'avons pas besoin de créer un fichier temporaire et de le renommer en

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
Ryan
la source
c'était la seule solution qui fonctionnait pour moi. c'est assez drôle que le nom de mon serveur soit spongebob.
Ömer An
11

S'il est acceptable de remplacer le fichier d'entrée:

Remarque:

  • Cela peut avoir des effets secondaires inattendus, notamment le remplacement potentiel d'un lien symbolique par un fichier normal, se retrouver avec des autorisations différentes sur le fichier et modifier la date de création (naissance) du fichier.

  • sed -i, comme dans la réponse du prince John Wesley , tente au moins de restaurer les autorisations d'origine, mais les autres limitations s'appliquent également.

Voici une alternative simple qui utilise un fichier temporaire (cela évite de lire le fichier d'entrée entier en mémoire comme le fait la solution de Shime ):

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

L'utilisation d'une commande de groupe ( { ...; ...; }) est légèrement plus efficace que l'utilisation d'un sous - shell ( (...; ...)), comme dans la solution de 0xC0000022L .

Les avantages sont:

  • Il est facile de contrôler si le nouveau texte doit être directement ajouté à la première ligne ou s'il doit être inséré en tant que nouvelle (s) ligne (s) (ajoutez simplement \nà l' printfargument).

  • Contrairement à la sedsolution, cela fonctionne si le fichier d'entrée est vide ( 0octets).


La sedsolution peut être simplifiée si l'intention est d'ajouter au début une ou plusieurs lignes entières au contenu existant (en supposant que le fichier d'entrée n'est pas vide):

sedLa ifonction de insère des lignes entières:

Avec 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

Une variante portable qui fonctionne également avec 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

Notez que le saut de ligne littéral après le \est obligatoire.


Si le fichier d'entrée doit être édité sur place (en conservant son inode avec tous ses attributs):

Utilisation du vénérable edutilitaire POSIX :

Remarque:

  • edlit invariablement le fichier d'entrée dans son ensemble en mémoire en premier.

Pour ajouter directement à la première ligne (comme avec sed, cela ne fonctionnera pas si le fichier d'entrée est complètement vide ( 0octets)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedmessages d'état supprimés .
  • Notez comment les commandes sont fournies edsous forme de document here-document ( <<EOF\n...\nEOF) sur plusieurs lignes , c'est-à-dire via stdin ; par défaut, l'expansion des chaînes est effectuée dans ces documents (les variables shell sont interpolées); citez le délimiteur d'ouverture pour supprimer cela (par exemple,<<'EOF' ).
  • 1 fait de la 1ère ligne la ligne courante
  • function seffectue une substitution de chaîne basée sur une expression régulière sur la ligne courante, comme dans sed; vous pouvez inclure des sauts de ligne littéraux dans le texte de substitution, mais ils doivent être \échappés.
  • wréécrit le résultat dans le fichier d'entrée (pour le test, remplacez wpar ,ppour imprimer uniquement le résultat, sans modifier le fichier d'entrée).

Pour ajouter une ou plusieurs lignes entières au début :

Comme avec sed, la ifonction ajoute invariablement une nouvelle ligne à la fin du texte à insérer.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 ifait 0(le début du fichier) la ligne courante et démarre le mode insertion ( i); notez que les numéros de ligne sont 1basés sur une autre base.
  • Les lignes suivantes sont le texte à insérer avant la ligne courante, se terminant par .sur sa propre ligne.
mklement0
la source
7

Probablement rien de intégré, mais vous pouvez écrire le vôtre assez facilement, comme ceci:

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

Quelque chose comme ça au moins ...

Rob I
la source
6
+1 pour utiliser $$ (le PID actuel) comme partie du nom de fichier temporaire. Si vous créez un script à usage général, cela empêchera un autre utilisateur d'écraser potentiellement le fichier temporaire en exécutant le même script en même temps.
E Brown
3
L'utilisation $$est cependant insuffisante pour le code de production (attaque de lien symbolique google); mais c'est certainement mieux qu'un nom de fichier statique.
tripleee
1
il suffit d'utilisermktemp
mewa
7

Dans certaines circonstances, le texte préfixé peut être disponible uniquement à partir de stdin. Ensuite, cette combinaison fonctionnera.

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

Si vous souhaitez omettre la teesortie, ajoutez > /dev/null.

sehari24jam
la source
J'aime vraiment cette réponse. C'est une façon très claire et claire de le faire. L'utilisation de tee est une solution naturelle au problème de la tentative de chat de la sortie vers le fichier d'entrée. Aussi pas de hacks avec changement de nom. Mieux que la solution la plus votée actuellement, car elle ne crée pas de nouveau fichier. C'est fondamentalement le plus proche >> que vous obtiendrez pour le préfixe au lieu d'ajouter.
DC2
C'est de loin la réponse la meilleure et la plus propre.
nerdoc
6
Y a-t-il une garantie qui teene clobe pas text.txtavant catde le lire? Je ne pense pas - ce qui rendrait cette solution dangereuse pour la santé du dossier.
Jonathan Leffler
3
@JonathanLeffler probablement non. J'ai essayé ce code à tester: 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. Si lenAvaut 1000000 (fichier de 1 Mo) et lenB10000 (texte de 10 Ko en préfixe), alors le fichier "a.txt" est écrasé par 20 Ko de lettres "b". C'est totalement cassé. Maintenant, si vous utilisez 1Mb a.txt et 1Mb de texte à ajouter, teeentre dans une boucle générant un fichier 7Gb +, j'ai dû arrêter la commande. Il est donc évident que le résultat est imprévisible pour les grandes tailles. Je n'ai aucune information si cela devrait fonctionner sur de petites tailles.
VasiliNovikov
3
Pour le dire en termes conceptuels: cette commande entraînera une perte de données si le fichier d'entrée est plus grand que la taille de la mémoire tampon du pipeline de votre système , qui est généralement de 64 Ko de nos jours.
mklement0 le
7

Solution:

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

Notez que cela est sûr sur tous les types d'entrées, car il n'y a pas d'extensions. Par exemple, si vous souhaitez ajouter un préfixe !@#$%^&*()ugly text\n\t\n, cela fonctionnera simplement:

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

La dernière partie à considérer est la suppression des espaces à la fin du fichier lors de la substitution de commande "$(cat file.txt)". Toutes les solutions de contournement pour cela sont relativement complexes. Si vous souhaitez conserver les nouvelles lignes à la fin de file.txt, voir ceci: https://stackoverflow.com/a/22607352/1091436

VasiliNovikov
la source
Aimer. Super portable et n'a pas besoin de générer un nouveau fichier. Merci!
pyb
1
Le seul problème avec cela survient si le contenu de file.txtest plus grand que ce qui peut être inséré dans la liste d'arguments printf.
Jonathan Leffler
6

Une autre façon d'utiliser sed:

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

Si la ligne à ajouter est multiligne:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile
Mert Nuhoglu
la source
Pourquoi pas sed -i '1ito be prepended' inFile- ou est-ce uniquement autorisé pour GNU sed?
utilisateur inconnu du
@userunknown: En standard sed, la icommande est suivie d'une barre oblique inverse et d'une nouvelle ligne, et chaque ligne d'entrée sauf la dernière se termine également par une barre oblique inverse. GNU sedautorise les raccourcis dans le sens que vous demandez, mais ce n'est que GNU sedqui le fait. '
Jonathan Leffler
3

Comme testé dans Bash (dans Ubuntu), si vous démarrez avec un fichier de test via;

echo "Original Line" > test_file.txt

vous pouvez exécuter;

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

ou, si la version de bash est trop ancienne pour $ (), vous pouvez utiliser des backticks;

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

et recevez le contenu suivant de "test_file.txt";

New Line
Original Line

Pas de fichier intermédiaire, juste bash / echo.

AlexanderESmith
la source
2
meilleure réponse, j'ai écrit ce one-liner pour faire pivoter mon fichier en utilisant cette réponse: for i in $ (<file2.txt); do echo "$ (echo $ i; cat file.txt)"> file.txt; terminé;
Aurangzeb
2

Une autre solution assez simple est:

    $ echo -e "string\n" $(cat file)
Tapoter
la source
Cela a fonctionné pour moi ... juste une légère modification, afin de conserver les multilignes du fichier d'entrée, vous devez l'envelopper entre guillemets, donc quelque chose comme ceci: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / file.txt;
Craig Wayne
2
Ne pas avoir l' catexpansion des paramètres entre guillemets doubles est une très bonne raison pour un vote défavorable . Ce code divise le contenu du fichier en mots et passe chacun de ces mots comme argument distinct à echo. Vous perdez les limites des arguments d'origine, vous perdez les nouvelles lignes / tabulations / etc., et les chaînes comme \tet \ndans le texte d'origine sont remplacées par des tabulations et des nouvelles lignes au lieu d'être conservées telles quelles.
Charles Duffy
1

Si vous aimez vi / vim, c'est peut-être plus votre style.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Ian Kelling
la source
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
Jim
la source
0

Je recommanderais de définir une fonction, puis de l'importer et de l'utiliser si nécessaire.

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
}

Ensuite, utilisez-le comme ceci:

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

Le contenu de votre fichier sera alors:

This is second
This is first

Je suis sur le point d'utiliser cette approche pour implémenter un programme de mise à jour du journal des modifications.

AwesomeBobX64
la source