s/$f/$g/
substitui a primeira ocorrência de $f
por $g
em cada linha. Se você quiser substituir apenas a primeira ocorrência de $f
no arquivo inteiro, será necessário dizer isso. Isso é o que você fez em sed
com 0,/$f/ s/$f/$g/
(substitua $f
por $g
até e incluindo a primeira ocorrência de $f
). Em Perl, você pode escrever isso de uma forma mais detalhada, mas mais fácil de entender como essa (observe: veja abaixo para citar questões):
perl -i -pe 'if ($n==0) {s/$f/$g/; $n=1;} elsif ($n==1) {s/$f/$h/; $n=2}'
Seu código sofre vários problemas de cotação; você terá problemas se seus nomes de arquivo contiverem espaços em branco, caracteres globbing ou caracteres não imprimíveis (como sequências de bytes que não existem na localidade atual). Felizmente, esses problemas são fáceis de resolver.
Primeiro, alguns problemas genéricos de shell. Sempre dê duas citações de substituições de variáveis "$foo"
e substituições de comandos "$(foo)"
a menos que você saiba por que precisa deixá-las sem aspas. Se você deixá-los sem aspas, o resultado é dividido em palavras separadas onde quer que haja espaço em branco, e cada palavra é tratada como um padrão glob. Portanto, a menos que a variável contenha uma lista de padrões glob separados por espaço em branco, coloque aspas duplas em torno dela. Além disso, recomendo usar $(…)
em vez de '…'
; eles são equivalentes, exceto que a cotação aninhada dentro de '…'
não é confiável (também, '
é facilmente confundida com '
).
Não analise a saída de ls
. Se você precisa agir em todos os arquivos em um diretório, o shell tem uma construção interna que funciona: globbing. Em vez de $(ls /path/to/directory)
, escreva /path/to/directory/*
. Isso gera nomes de arquivos com o caminho do diretório; quase sempre é o que você precisa, e se não o fizer, você pode chamar cd
antes ou remover todo ou parte do diretório. Abaixo, uso ${f#*/*/}
, o que significa $f
com o prefixo mais curto correspondendo */*/
retirado.
for f in .templates/template_text/*; do
g=$(cat "$f")
h=$(cat ".templates/template_html/${f#*/*/}")
find to_process/ -type f …
done
Com find
, você pode usar a construção mais simples -exec
, embora -print0
combinado com xargs -0
também funcione. Não use xargs
sem -0
, já que espera entrada citada de uma forma peculiar que find
não produz.
find to_process/ -type f -exec perl … {} +
A próxima questão é que você está inserindo as strings $f
, $g
e $h
diretamente em sua expressão regular sed ou perl. Isso está errado: essas variáveis não contêm uma expressão regular com o delimitador ( /
em ambos os casos) entre aspas. Com sed, você precisaria fazer uma passagem de citações nas cadeias de caracteres, adicionando uma barra invertida antes de qualquer /*.\[
em $f
e antes de qualquer \&/
em $g
e $h
. Com o Perl, existe uma maneira mais simples: passar os valores pelo ambiente e não se esqueça de dizer ao Perl que o que você tem é uma string e não um regexp.
export f g h
find to_process/ -type f -exec perl -i -e '
if ($n==0) {s/\Q$ENV{f}/$ENV{g}/; $n=1;}
elsif ($n==1) {s/\Q$ENV{f}/$ENV{h}/; $n=2}}
' {} +