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".
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.
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.
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
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).
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
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 r
ead 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 sed
s 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 r
ead, et sed
l'appliquera et r
ead dans l'ordre de leur script. C'est un peu plus compliqué cependant - cela implique d'utiliser un sed
pour a
ajouter 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 q
uits entrée. Son travail consiste à ...
s/[]^$&\./*[]/\\&/g;H
sed
devra interpréter chaque bit qu'il lit littéralement pour bien faire les choses. Une fois cela fait, mettez une copie dans H
old espace.s|.*|/&/!p;//!d|p; x
sed
à p
rint chaque ligne d'entrée !
mais le /&/
celui que nous venons de protéger; puis à d
supprimez tout de même. p
imprimez les commandes au deuxième sed
, puis e x
changez les tampons h
old et pattern pour travailler sur notre copie enregistrée.s|.|r file1&a\\&|p;q
\n
ewline car sed
en aura ajouté un avant lorsque nous H
eld la ligne précédente. On insère donc la commande r file1
et suivez-le avec notre \n
ewline puis la commande a\\
pour a
ppend suivi également d'un \n
ewline. Tout le reste de notre ligne H
eld suit la dernière \n
ewline.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 a
ppend. Pour cette ligne particulière deux écritures retardées vers la sortie standard sont planifiées - le premier est le r
ead 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.
[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
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 .
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
awk '/Pointer/{system("cat File1")}1' File2