web-dev-qa-db-fra.com

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

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, File2 le contenu 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 coller le contenu de File1 dans File2 avant la ligne contenant "Pointer".

39
user1228191

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 


Avec GNU sed

sed '/Pointer/e cat file1' file2

Selon manuel pour e [command]

Notez que, contrairement à la commande r, la sortie de la commande sera imprimée immédiatement; la commande r retarde la sortie à la fin du cycle en cours.

37
jfg956

Sans utiliser sed ou 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 sed de awk solution.

18
jfg956

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

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

Pour imprimer le fichier interne après la ligne Pointer, changez simplement l'ordre des modèles (vous devez ajouter un point-virgule pour obtenir l'action par défaut), et vous pouvez supprimer la variable line:

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

Et juste parce que personne n'a encore utilisé Perl,

# 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
11
Kevin

Un travail facile pour ed:

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

-r file1 lit dans le fichier spécifié après la ligne adressée, qui dans ce cas est la ligne précédant la première ligne correspondant à Pointer. Donc, cela insérera le contenu de file2 une seule fois même si Pointer se produit sur plusieurs lignes. Si vous souhaitez l'insérer avant chaque ligne correspondante, ajoutez l'indicateur g lobal:

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

Remplacer ,p avec w si vous souhaitez modifier le fichier sur place.


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

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

qui fonctionne également très bien (comme r fera 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 ligne ext N 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 file2 contenu avant chaque ligne correspondante. Pour l'insérer uniquement avant la première ligne correspondante, vous pouvez utiliser un oop l et simplement insérer la ligne ext n jusqu'à la fin du fichier:

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

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

7
don_crissti

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

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

Il y a plusieurs façons de procéder avec w/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 aura inévitablement le même problème avec la dernière ligne que @don_crissti note, cependant, parce que N incrémente le cycle de ligne et le read commande 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 les seds n'interpréteront pas le - signifie entrée standard, mais beaucoup le font. ( POSIX ditsed devrait supporter - signifie entrée standard si l'implémenteur souhaite - pour 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 sed l'appliquera et read dans l'ordre de leur script. C'est un peu plus compliqué cependant - cela implique d'utiliser un sed pour aajouter la correspondance Pointer à la sortie d'un autre sed dans 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 sed un script, que le second sed lit sur l'entrée standard (peut-être ... ) et s'applique à son tour. Le premier sed ne fonctionne que sur la première correspondance pour Pointer trouvé, et ensuite quits entrée. Son travail consiste à ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Assurez-vous que tous les caractères du modèle sont bien protégés par une barre oblique inverse, car le second sed devra interpréter chaque bit qu'il lit littéralement pour bien faire les choses. Une fois cela fait, mettez une copie dans Hold espace.
  2. s|.*|/&/!p;//!d|p; x
    • Dites au second sed à print chaque ligne d'entrée !mais le /&/ celui que nous venons de protéger; puis à dsupprimez tout de même. pimprimez les commandes au deuxième sed, puis e xchangez les tampons hold et pattern pour travailler sur notre copie enregistrée.
  3. s|.|r file1&a\\&|p;q
    • Le seul personnage avec lequel nous travaillons ici est un \newline car sed en aura ajouté un avant lorsque nous Held la ligne précédente. On insère donc la commande r file1 et suivez-le avec notre \newline puis la commande a\\ pour append suivi également d'un \newline. Tout le reste de notre ligne Held suit la dernière \newline.

Le script que le premier écrit ressemble à ceci:

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

Fondamentalement, le second sed imprimera chaque ligne, mais celui le premier sed le définira sur append. Pour cette ligne particulière deux écritures retardées vers la sortie standard sont planifiées - le premier est le read de file1 et le second est une copie de la ligne que nous voulons après. Le premier traitement de sed n'est même pas nécessaire dans ce cas (voir? Pas de barres obliques inverses) mais il est important de s'échapper en toute sécurité dans le comme je le fais ici chaque fois qu'une correspondance de motif est réutilisée en entrée.

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

4
mikeserv

[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
3
Jim.Y

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

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

Cela remplacera chaque MODIFICATION occurrence dans fichier d'origine par le contenu de file2insert. Supprimez le dernier g de sed pour remplacer uniquement la première occurrence de CHANGEME .

2
nrc

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
2
Amos Folarin
 awk '/Pointer/{system("cat File1")}1' File2
0
dedowsdi