Altera o texto em um único número de linha em vários arquivos de texto

4

Suponha que eu tenha dez scripts de shell bash: script1.sh , script2.sh , ..., script10.sh . Inicialmente, todos os dez arquivos são idênticos.

Agora, gostaria de fazer duas alterações em cada script:

  1. Em cada arquivo, gostaria de alterar uma linha específica (digamos, a linha 8) - ou seja, excluir o que está na linha 8 e substituí-la por uma string "constante" que eu especifico, como "This is line 8." Isso é semelhante a esta pergunta , mas lá eles queriam substituir "AAA" por "BBB" , enquanto eu gostaria de substituir a linha 8 (o que quer que seja) por "This is line 8." .

  2. Em cada arquivo, gostaria de alterar outra linha específica (digamos, linha 21) e substituí-la por uma sequência "variável" que especifico. Por exemplo, em script1.sh , quero alterar a linha 21 para "XYZ" ; em script2.sh eu quero mudar a linha 21 para "PQR" ; e em script3.sh eu quero mudar a linha 21 para "ABC" . Essencialmente, isso é apenas muitas chamadas para a função em (1) acima - exceto que eu estaria fazendo a alteração em um arquivo individual em vez de em todos os arquivos, e especificando dez cadeias diferentes do que apenas um. Então, para obter (2) aqui, talvez eu apenas chame (1) dez vezes diferentes com parâmetros diferentes.

Estou interessado em soluções que usem programas Linux normalmente disponíveis como bash , vi , awk , gawk , etc.

    
por Andrew 21.06.2013 / 18:16

3 respostas

5
for file in f1 f2; do
  sed -i '8s/^.*$/foo/' "$file"
done
    
por 21.06.2013 / 18:22
2

Usando o awk

for file in script1.sh script2.sh script3.sh ... script10.sh; do
    temp=$(mktemp)
    awk '
        BEGIN {
            line8 = "This is line 8"
            line21["script1.sh"] = "XYZ"
            line21["script2.sh"] = "PQR"
            line21["script3.sh"] = "ABC"
            # ... 
        }
        FNR == 8 { print line8; next }
        FNR == 21 && FILENAME in line21 { print line21[FILENAME]; next }
        {print}
    '  "$file" > "$temp" && mv "$temp" "$file"
done

ou, usando bash e sed

# bash variables to store new values. requires bash v4 for the associative array
line8="This is line 8"
declare -A line21=(
    [script1.sh]="XYZ"
    [script2.sh]="PQR"
    [script3.sh]="ABC"
    # ...
)
# then use sed
for file in script1.sh script2.sh script3.sh ... script10.sh; do
    sed -i "8s/.*/$line8/; 21s/.*/${line21[$file]}/" "$file"
done

Com a solução sed, você precisa ter cuidado para que as novas linhas não contenham "/" caracteres,    como isso vai quebrar o comando s/// . Embora você possa usar diferentes    caracteres delimitadores, como s|pattern|replacement| (nesse caso, o    mesmo aviso aplica-se, recursivamente)

ed também funciona:

for file in script1.sh script2.sh script3.sh ... script10.sh; do
    ed "$file" <<ED_COMMANDS
8d
8i
$line8
.
21d
21i
${line21[$file]}
.
w
q
ED_COMMANDS
done
    
por 21.06.2013 / 19:46
2

Responderei a ambas as perguntas, mas, como regra geral, não combine perguntas como essa, divida-as em postagens separadas.

Questão 1

  1. Perl:

    for f in *.sh; do 
      perl -i -lne '$.==8 ? print "This is line 8" : print;' "$f"; 
    done
    
  2. awk e variantes:

    for f in *.sh; do 
      awk '{if(NR==8){print "This is line 8"}else{print}}' "$f" > fifo && 
      mv fifo "$f";
    done
    

Questão 2

Primeiro, criei um arquivo com as substituições que quero fazer. Por exemplo:

script1.sh  foo1
script2.sh  foo2
script3.sh  foo3
script4.sh  foo4

Em seguida, leia esse arquivo para realizar as substituições:

while read f s; do 
 export s;
 perl -i -lne '$.==8 ? print "$ENV{s}" : print;' "$f";
done < sublist.txt
    
por 21.06.2013 / 19:58