Comment insérer le contenu d'un fichier dans un autre fichier avant un motif (marqueur)?

32

File1 Contenu:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2 Contenu:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

Après l'exécution du script perl / shell, le File2contenu devrait devenir:

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"

c'est-à-dire collez le contenu de File1in File2avant la ligne qui contient "Pointer".

user1228191
la source
1
Également demandé à StackOverflow
Glenn Jackman
J'ai déjà voté la question SO pour la clôture sur cette raison, il n'y a plus de raison de fermer cette question. Btw, la question est de bien pire qualité sur le SO, donc la logique dicte de fermer cela et pas cela.
Peterh dit de réintégrer Monica

Réponses:

33

sed a une fonction pour cela, et peut faire la modification en ligne:

sed -i -e '/Pointer/r file1' file2

Mais cela place votre ligne de pointeur au-dessus du fichier1. Pour le mettre ci-dessous, sortie ligne à retard:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 
jfg956
la source
8
Pourriez-vous s'il vous plaît expliquer quoi -e 1x -e '2,${x;p}' -e '${x;p}'faire? Je comprends que vous échangez des trucs dans le tampon de motif, puis l'imprimez, mais je ne sais pas quoi ni pourquoi vous avez ajouté l'option silencieuse -nau début.
hdl
@ jfg956 Est-il possible de simplement remplacer et supprimer la partie 'Pointeur' du fichier d'origine. Je peux comprendre cela avec un deuxième balayage de sed, mais est-il possible de le faire en une seule fois?
Alexander Cska
17

Sans utiliser sedou awk...

Tout d'abord, trouvez votre ligne sur laquelle se trouve votre motif:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

Ensuite, utilisez 3 commandes pour sortir le résultat souhaité:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

Cela a l'inconvénient d'accéder à 3 fois le fichier file2, mais peut - être plus clair qu'une sedde awksolution.

jfg956
la source
10

awkrend cela assez facile.
Insérez la ligne avant le fichier:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

Pour que le fichier interne s'imprime après la Pointerligne, changez simplement l'ordre des motifs (vous devez ajouter un point-virgule pour obtenir l'action par défaut) et vous pouvez supprimer la linevariable:

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

Et juste parce que personne n'a perlencore utilisé ,

# insert file before line
perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile
Kevin
la source
son fonctionnement, mais sa suppression de la ligne contenant le pointeur
user1228191
de même, comment coller le contenu du fichier 1 dans le fichier 2 après cette ligne contenant "Pointer" en utilisant awk
user1228191
@ user1228191 Correction du premier, ajout du second.
Kevin
La version «perl» ne semble pas fonctionner. system("cat innerfile")renvoie le innerfileà la console. Est-ce que je manque quelque chose?
kaartic
commande awk [gawk '/ <body> / {while (ligne getline <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] boucle et imprime simplement des millions de lignes . gawk V4.2.0 Qu'est-ce que je manque ici?
JESii
7

Un travail facile pour ed:

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1lit dans le fichier spécifié après la ligne adressée, qui dans ce cas est la ligne précédant la première correspondance de ligne Pointer. Ainsi, cela insérera le contenu d' file2une seule fois même si cela Pointerse produit sur plusieurs lignes. Si vous souhaitez l'insérer avant chaque ligne correspondante, ajoutez le gdrapeau lobal:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

Remplacez ,ppar wsi vous souhaitez modifier le fichier sur place.


La sedréponse acceptée fonctionne dans la plupart des cas, mais si le marqueur est sur la dernière ligne, la commande ne fonctionnera pas comme prévu: elle insérera le contenu File1après le marqueur.
J'ai d'abord essayé avec:

sed '/Pointer/{r file1
N}' file2

qui fonctionne également très bien (comme le rfera sa magie à la fin du cycle) mais a le même problème si le marqueur est sur la dernière ligne (il n'y a pas de Nligne ext après la dernière ligne). Pour contourner cela, vous pouvez ajouter une nouvelle ligne à votre entrée:

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)

Cela insérera du file2contenu avant chaque ligne correspondante. Pour l'insérer uniquement avant la première ligne correspondante, vous pouvez utiliser un loop et simplement tirer la nligne ext jusqu'à la fin du fichier:

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)

Avec ces sedsolutions, vous perdez la possibilité de modifier sur place (mais vous pouvez rediriger vers un autre fichier).

don_crissti
la source
6

Utilisez une boucle pour lire les lignes du fichier2. Si vous trouvez une ligne commençant par Pointer, imprimez le fichier 1. Ceci est illustré ci-dessous:

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2
dogbane
la source
4

Il y a plusieurs façons de procéder à ce sujet avec sed. Une façon est une lecture différée, comme recommandé dans la réponse acceptée. Il pourrait également s'écrire comme:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

... avec un peu d'anticipation explicite au lieu de l'anticipation implémentée ailleurs avec le tampon de retenue. Cela inévitablement le même problème avec la dernière ligne que les notes @don_crissti, cependant, parce que N ne incrément le cycle de la ligne et la rcommande DEE est appliquée par numéro de ligne.

Vous pouvez le contourner:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

Tous sedn'interpréteront pas le -pour signifier entrée standard, mais beaucoup le font. ( POSIX indique sed que le support devrait -signifier entrée standard si l'implémenteur veut -signifier entrée standard ???)

Une autre façon consiste à gérer le contenu ajouté dans l'ordre. Il existe une autre commande qui planifie la sortie de la même manière que read, et sedl'appliquera et rlira dans l'ordre de leur script. C'est un peu plus compliqué cependant - cela implique d'utiliser l'un sedpour amettre le Pointermatch en correspondance avec la sortie d'un autre seddans son script.

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file

Donc, fondamentalement, le premier sedécrit le second sedun script, que le second sedlit sur l'entrée standard (peut - être ...) et s'applique à son tour. Le premier sedne fonctionne que sur la première correspondance pour les éléments Pointertrouvés, puis les qentrées. Son travail consiste à ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Assurez-vous que tous les caractères du modèle sont échappés par une barre oblique inversée, car le second seddevra interpréter chaque bit qu'il lit littéralement pour le faire correctement. Une fois cela fait, mettez une copie dans l' Hancien espace.
  2. s|.*|/&/!p;//!d|p; x
    • Dites à la seconde sedd' pimprimer chaque ligne d'entrée, !mais /&/celle que nous venons de sauvegarder en mode; puis à dtout de même disparaître. préimprimez les commandes à la seconde sed, puis xmodifiez les hanciens tampons et les tampons de modèle pour travailler sur notre copie enregistrée.
  3. s|.|r file1&a\\&|p;q
    • Le seul caractère avec \nlequel nous travaillons ici est une ligne électronique, car sedelle en aura ajouté une Havant la dernière ligne. Nous insérons donc la commande r file1et la suivons avec notre \newline puis la commande a\\pour append suivie également d'une \newline. Tout le reste de notre Hligne de champ suit cette dernière ligne \nélectronique.

Le script que le premier écrit ressemble à ceci:

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

Fondamentalement, la seconde sedimprimera chaque ligne, mais celle que la première sedconfigure pour append. Pour cette ligne particulière, deux écritures différées vers la sortie standard sont planifiées - la première est la rtête de lecture file1et la seconde est une copie de la ligne que nous voulons après. Le premier sedraclage n'est même pas nécessaire dans ce cas (voir? Pas de barre oblique inverse) mais il est important de s'échapper en toute sécurité comme je le fais ici chaque fois qu'une correspondance de modèle est réaffectée en entrée.

Quoi qu'il en soit, alors ... il y a plusieurs façons.

Mikeserv
la source
2

C'est assez simple avec AWK:

File1 dans File2 avant pattern = "Pointer"

Chargez d'abord le contenu de File1 dans une variable

f1="$(<File1)"

puis faites l'insertion

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(Ou, si vous souhaitez insérer File1 après "Pointer")

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2
Amos Folarin
la source
2

ma façon préférée: les modèles .

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

Cela remplacera chaque occurrence CHANGEME dans origfile par le contenu de file2insert . Retirez le dernier g de sed pour remplacer uniquement la première occurrence de CHANGEME .

nrc
la source
Comment pouvez-vous utiliser $xdans votre première commande, alors qu'elle n'est définie que dans votre deuxième commande?
Totor
le "$ x" dans la première commande n'est qu'un espace réservé à évaluer par envsubst dans la deuxième commande. Avec des guillemets simples du script sed, $ x n'est pas évalué par votre shell.
nrc
2

[Insérer le contenu du fichier dans un autre fichier AVANT le modèle]

sed -i '/PATTERN/r file1' -e //N file2

[Après le motif]

sed -i '/PATTERN/r file1' file2
Jim.Y
la source
Nfonctionne bien, mais pas si le MOTIF correspond à la dernière ligne d'entrée
Sundeep
0
 awk '/Pointer/{system("cat File1")}1' File2
dedowsdi
la source