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

32

File1 conteúdo:

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

File2 conteúdo:

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

Após a execução do script perl / shell, o File2conteúdo deve 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 File1in File2antes da linha que contém "Ponteiro".

user1228191
fonte
11
Também perguntei no StackOverflow
glenn jackman
Também stackoverflow.com/questions/11243102/…
Necreaux 10/16/16
Já votei a questão SO para o encerramento por esse motivo, não há mais motivo para encerrar esta questão. Aliás, a questão é de qualidade muito pior no SO, de modo que a lógica determina fechar isso e não isso.
peterh diz restabelecer Monica

Respostas:

33

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 de ponteiro 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 
jfg956
fonte
8
Poderia explicar o que -e 1x -e '2,${x;p}' -e '${x;p}'fazer? Entendo que você troca coisas no buffer de padrão e depois imprime, mas não sei o que nem por que você adicionou a opção silenciosa -nno início.
HDL
@ jfg956 É possível substituir e excluir a parte 'Ponteiro' do arquivo original. Isso eu posso descobrir com uma segunda varredura do sed, mas é possível fazê-lo de uma só vez?
Alexander Cska
17

Sem usar sedouawk ...

Primeiro, encontre a linha na qual está o 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

Isto tem a desvantagem de acessar 3 vezes o arquivo file2, mas pode ser mais claro do que a sedde awksolução.

jfg956
fonte
10

awktorna isso bastante fácil.
Insira 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 Pointerlinha, basta alternar a ordem dos padrões (você precisa adicionar um ponto-e-vírgula para obter a ação padrão) e soltar a linevariável:

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

E só porque ninguém usou perlainda,

# 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
fonte
está funcionando, mas está sendo removida a linha que contém o ponteiro
user1228191 28/02/2012
da mesma forma, como Cole o conteúdo do arquivo 1 no arquivo 2 depois disso "Pointer" contendo line usando awk
user1228191
@ user1228191 Corrigido o primeiro, adicionado o segundo.
28712 Kevin Kevin
A versão 'perl' não parece estar funcionando. system("cat innerfile")envia o innerfilepara o console. Estou esquecendo de algo?
kaartic
comando awk [gawk '/ <body> / {while (getline line <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] apenas faz um loop e imprime milhões de linhas . gawk V4.2.0 O que estou perdendo aqui?
JESii 13/0218
7

Um trabalho fácil para ed:

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

-r file1lê no arquivo especificado para depois da linha endereçada, que neste caso é a linha anterior à primeira linha correspondente Pointer. Portanto, isso inserirá o conteúdo de file2apenas uma vez, mesmo que Pointerocorra em várias linhas. Se você deseja inseri-lo antes de cada linha correspondente, adicione gflag lobal:

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

Substitua ,ppor wse desejar editar o arquivo no local.


A sedresposta aceita funciona na maioria dos casos, mas se o marcador estiver na última linha, o comando não funcionará conforme o esperado: ele inserirá o conteúdo File1após o marcador.
Eu tentei inicialmente com:

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

que também funciona bem (como rfará sua mágica no final do ciclo), mas tem o mesmo problema se o marcador estiver na última linha (não há Nlinha 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 inserirá o file2conteúdo antes de cada linha correspondente. Para inseri-lo somente antes da primeira linha correspondente, você pode usar loop e puxar a nlinha ext até chegar ao final do arquivo:

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

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

don_crissti
fonte
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
dogbane
fonte
4

Existem algumas maneiras de resolver esse problema sed. Uma maneira é uma leitura atrasada, conforme recomendado na resposta aceita. Também poderia ser escrito como:

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

... com um olhar explícito e pouco explícito, em vez do olhar para trás implementado em outro lugar com o buffer de espera. Isso inevitavelmente terá o mesmo problema com a última linha que o @don_crissti observa, porqueN faz incremento do ciclo de linha eor comando ead é aplicada por número da linha.

Você pode contornar isso:

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

Nem todos os seds interpretam o -significado de entrada padrão, mas muitos o fazem. ( POSIX diz que sed deve apoiar- deve significar entrada padrão se o implementador quiser -significar entrada padrão ???)

Outra maneira é manipular o conteúdo anexado em ordem. Há outro comando que agenda a saída da mesma maneira que o read faz, e sedo aplicará e rna ordem em que estão no script. No entanto, é um pouco mais complicado - implica o uso de um sedpara apendurar a Pointercorrespondência na saída de outrosed 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 sedescreve o segundo, sedum script, que o segundo sedlê na entrada padrão (talvez ...) e se aplica por sua vez. O primeiro sedfunciona apenas na primeira correspondência para Pointerencontrado e, depois, qutiliza a entrada. Seu trabalho é ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Certifique-se de que todos os caracteres padrão sejam escapados com segurança pela barra invertida, porque o segundo sedprecisará interpretar cada parte que lê literalmente para acertar. Feito isso, coloque uma cópia no Hespaço antigo.
  2. s|.*|/&/!p;//!d|p; x
    • Diga ao segundo sedpara criar ptodas as linhas de entrada, !exceto a /&/que acabamos de proteger; e depois dexcluir tudo da mesma forma. print os comandos no segundo e sed, em seguida, e xaltere os hbuffers antigo e padrão para trabalhar em nossa cópia salva.
  3. s|.|r file1&a\\&|p;q
    • O único caractere com o qual trabalhamos aqui é um \newline, porque ele sedserá anexado quando o Hcampo for delimitado anteriormente. Então, inserimos o comando r file1e o seguimos com nosso\n ewline, depois o comando a\\para append seguido também por um \newline. Todo o resto da nossa Hlinha de campo segue a última linha de \new.

O script que o primeiro escreve é ​​mais ou menos assim:

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

Basicamente, o segundo sedimprime todas as linhas, mas a primeira sedconfigura-a como apendente. Para essa linha específica, estão programadas duas gravações atrasadas na saída padrão - a primeira é a read de file1eo segundo é uma cópia da linha queremos depois. O primeiro sedmédico não é necessário neste caso consulta (veja? Sem barras invertidas), mas é importante escapar com segurança da maneira que faço aqui sempre que uma correspondência de padrão é redirecionada como entrada.

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

mikeserv
fonte
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)"

então faça a inserção

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

(Ou, se você deseja inserir Arquivo1 após "Ponteiro")

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

minha maneira preferida: modelagem .

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

Isso substituirá todas as ocorrências de CHANGEME no origfile pelo conteúdo de file2insert . Remova o último g do sed para substituir apenas a primeira ocorrência de CHANGEME .

nrc
fonte
Como você pode usar $xno seu primeiro comando, quando ele é definido apenas no seu segundo comando?
Totor 21/10
o "$ x" no primeiro comando é apenas um espaço reservado para ser avaliado pelo envsubst no segundo comando. Com aspas simples do script sed, $ x não é avaliado pelo seu shell.
Nrc 21/10
2

[Inserir 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
Jim.Y
fonte
Nfunciona bem, mas não se o
padrão
0
 awk '/Pointer/{system("cat File1")}1' File2
dedowsdi
fonte