Como inserir o conteúdo de um arquivo em outro arquivo antes de um padrão (marcador)?

26

File1 contents:

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

File2 contents:

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

Após a execução do script perl / shell, File2 content deverá se tornar:

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"

ou seja, cole o conteúdo de File1 em File2 antes da linha que contém "Pointer".

    
por user1228191 28.02.2012 / 06:38

9 respostas

30

sed tem uma função para isso e pode fazer a modificação em linha:

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

Mas isso coloca sua linha Pointer acima do arquivo1. Para colocá-lo abaixo, retarde a saída da linha:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 
    
por 28.02.2012 / 08:46
13

Sem usar sed ou awk ...

Primeiro, encontre sua linha na qual é seu padrão:

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

Em seguida, use 3 comandos para gerar o resultado desejado:

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

Isso tem a desvantagem de acessar 3 vezes o arquivo file2 , mas pode ser mais claro do que sed de awk solution.

    
por 28.02.2012 / 08:54
8

awk torna isso bastante fácil.
Inserir a linha antes do arquivo:

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

Para imprimir o arquivo interno após a linha Pointer , basta alternar a ordem dos padrões (você precisa adicionar um ponto-e-vírgula para obter a ação padrão) e descartar a variável line :

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

E só porque ninguém usou perl ainda,

# 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
    
por 28.02.2012 / 07:22
7

Um trabalho fácil para ed :

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

-r file1 lê no arquivo especificado após a linha endereçada, que neste caso é a linha anterior à primeira linha correspondente a Pointer . Portanto, isso irá inserir o conteúdo de file2 apenas uma vez, mesmo se Pointer ocorrer em várias linhas. Se você quiser inseri-lo antes de cada linha correspondente, adicione g lobal flag:

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

Substitua ,p por w se você quiser editar o arquivo no local.

A resposta sed aceita funciona na maioria dos casos, mas se o marcador estiver na última linha, o comando não funcionará como esperado: ele inserirá o conteúdo de File1 após o marcador.
Eu tentei inicialmente com:

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

que também funciona bem (como r fará sua mágica no final do ciclo) mas tem o mesmo problema se o marcador estiver na última linha (não há N ext após a última linha). Para contornar isso, você pode adicionar uma nova linha à sua entrada:

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)

Isso irá inserir file2 content antes de cada linha correspondente. Para inseri-lo somente antes da primeira linha correspondente, você poderia usar um l oop e apenas puxar a linha n ext até chegar ao fim do arquivo:

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

Com essas soluções sed , você perde a capacidade de editar no local (mas pode redirecionar para outro arquivo).

    
por 31.03.2015 / 02:29
6

Use um loop para ler as linhas no arquivo2. Se você encontrar uma linha começando com Pointer , imprima o arquivo1. Isso é mostrado abaixo:

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

Existem algumas maneiras de fazer isso com sed . Uma maneira é uma leitura atrasada, como é recomendado na resposta aceita. Também poderia ser escrito como:

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

... com um pouco de look-ahead explícito em vez do look-behind implementado em outro lugar com o buffer de espera. Isso inevitavelmente terá o mesmo problema com a última linha que @don_crissti observa, porque N faz incrementar o ciclo de linha e o comando r ead é aplicado por número de linha.

Você pode contornar isso:

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

Nem todos os sed s interpretarão o - para indicar a entrada padrão, mas muitos o fazem. ( POSIX diz sed deve suportar - para indicar o padrão se o implementador quiser - para significar padrão-em ???)

Outra maneira é manipular o conteúdo acrescentado em ordem. Existe outro comando que agenda a saída da mesma maneira que r ead, e sed irá aplicá-lo e r na ordem em que estão em script. Porém, é um pouco mais complicado - implica em usar uma sed para a ppend a correspondência Pointer para a saída de outro sed em seu 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

Então, basicamente o primeiro sed escreve o segundo script sed a, que o segundo sed lê na entrada padrão (talvez ...) e aplica-se por sua vez. O primeiro sed só funciona na primeira correspondência de Pointer encontrado e depois q uits de entrada. Seu trabalho é ...

  1. %código%
    • Certifique-se de que todos os chars padrão estejam com escape de barra invertida com segurança, porque o segundo s/[]^$&\./*[]/\&/g;H precisará interpretar cada bit literalmente para acertar. Feito isso, coloque uma cópia em sed old space.
  2. %código%
    • Diga ao segundo H a s|.*|/&/!p;//!d|p; x rint a cada linha de entrada sed , mas a p que acabamos de padronizar; e depois para ! elete tudo igual. /&/ rint os comandos no segundo d , depois e p alteram os buffers sed old e padrão para trabalhar em nossa cópia salva.
  3. %código%
    • O único caractere com o qual trabalhamos aqui é um x ewline, porque h terá um prepended quando s|.|r file1&a\&|p;q da linha antes. Então, inserimos o comando \n e o seguimos com o nosso sed ewline, depois o comando H para r file1 ppend seguido também por \n ewline. Todo o restante de nossa linha a\ segue a última a ewline.

O script que o primeiro escreve é parecido com isto:

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

Basicamente, o segundo \n imprime todas as linhas, mas a primeira H configura para \n ppend. Para essa linha em particular, duas gravações atrasadas para o padrão são programadas - a primeira é a sed ead de sed e a segunda é uma cópia da linha que quero depois disso. A primeira manipulação de a nem é necessária neste caso (veja 'sem barras invertidas) mas é importante escapar com segurança da maneira que eu faço aqui sempre que uma correspondência de padrão é reaproveitada como entrada .

Enfim, então ... existem algumas maneiras.

    
por 31.03.2015 / 05:36
2

minha maneira preferida: modelagem .

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

Isto irá substituir todas as ocorrências CHANGEME em origfile com o conteúdo de file2insert . Remova o último g do sed para substituir apenas a primeira ocorrência de CHANGEME .

    
por 21.10.2016 / 14:53
2

Isso é bastante simples com o AWK:

Arquivo1 no Arquivo2 antes do padrão="Ponteiro"

Primeiro, carregue o conteúdo do arquivo1 em uma variável

f1="$(<File1)"

faça a inserção

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

(Ou, se você quiser inserir o arquivo 1 depois de "Ponteiro")

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2
    
por 25.06.2015 / 16:55
0

[Insira o conteúdo do arquivo em outro arquivo ANTES do padrão]

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

[depois do padrão]

sed -i '/PATTERN/r file1' file2
    
por 15.11.2017 / 01:02