Aqui está meu comando (interromper intencionalmente):
grep FOO "/Users/gjtorikian/blah" -l | xargs sed -i "" "/FOO/{s/FOO/BAR/g; w /dev/stdout }"
No pico -level: grep
para FOO no diretório blah
; canalize apenas o nome do arquivo (por causa de -l
) para sed
; sed
executa uma substituição em linha (-i ""
) e imprime apenas o termo alterado para /dev/stdout
.
Se eu omitisse o -l
e pipe, eu recebo de grep
:
/Users/gjtorikian/blah/baz.cs:1:FOO /Users/gjtorikian/blah/bar.js:1:FOO
O que eu quero é sed
para realizar a substituição embutida e, em seguida, mostrar-me o arquivo e o termo substituído; por exemplo:
/Users/gjtorikian/blah/baz.cs:1:BAR /Users/gjtorikian/blah/bar.js:1:BAR
Isso é possível? Se for importante, prefiro mantê-lo apenas com grep
/ sed
. Preciso fazer um segundo grep
após o ?
Comentários
- Não ' não vejo uma maneira de obtenha o nome do arquivo atual em sed, então você provavelmente precisará de um segundo grep.
- Além disso, você não ' não precisa do
''
, que ' é o padrão. - No OS X,
''
não é o padrão .
Resposta
Uma possibilidade seria passar a saída de grep
para um sed
filtro separado.
grep -l FOO "/Users/gjtorikian/blah/"* | { tee /dev/fd/3 | xargs sed -i -e "/FOO/{" -e "s/FOO/BAR/g" -e "w /dev/stdout" } 3>&1 | sed "s/:FOO$/:BAR/"
Você poderia fazer sed
imprimir o número da linha (com o comando =
) quando ele encontrar uma correspondência e prosseguir pós-processamento.
Provavelmente seria mais claro usar o awk.
for x in "/Users/gjtorikian/blah/"*; do awk " sub(/FOO/, "BAR") {found=1; print FILENAME ":" NR ":" "BAR" >"/dev/stderr"} 1 {print} END {exit(!found)} " "$x" >"$x.tmp" && mv "$x.tmp" "$x" done
Resposta
Como eu “mn ou falante nativo de inglês, provavelmente não entendi.
Para “grep” um diretório você precisa de “-r”. O uso de “-l” imprime apenas o nome do arquivo e interrompe o grep após a primeira ocorrência.
# pattern=/home ; grep -l "$pattern" /etc/[a-z]* 2>/dev/null | while read line ; do echo "$line:$pattern" ; done /etc/adduser.conf:/home /etc/fstab:/home /etc/fstab~:/home /etc/libuser.conf:/home /etc/mpd.conf:/home /etc/mpd.conf~:/home /etc/mtab:/home /etc/netscsid.conf:/home /etc/passwd:/home /etc/passwd-:/home /etc/sudoers:/home /etc/sudoers.tmp~:/home
Resposta
Acho que você está complicando demais o problema tentando resolvê-lo de uma vez. Você simplesmente pode fazer um
grep -H -n /Users/gjtorikian/blah | sed "s/\(^.*?:[0-9]+?:\).*FOO.*/\1BAZ/"
para obtenha a lista de arquivos com números de linha e substituições (isso deve funcionar, desde que seus nomes de arquivo não contenham dois pontos, mas é uma má ideia no Mac OS de qualquer maneira …). Depois disso, você pode emitir um
sed -i "" "s/FOO/BAR/g" /Users/gjtorikian/blah
Nenhum grep e xarg é necessário aqui (você pode fazer um “find … | xarg” se tiver muitos arquivos, no entanto). Se você estiver preocupado sobre as duplicações, você pode colocar as duas linhas em um script ou função e fazer substituições de variáveis.
Comentários
Resposta
Substitua recursivamente FOO por BAR no diretório atual (modo silencioso)
grep -rl "FOO" | xargs sed -i "s/FOO/BAR/"
Resposta
Uma linha
grep -n FOO * -R|awk -F\: "{ print $1 }"|sort|uniq|while read file; do sed -i "s/FOO/BAR/g" $file; done
sed -i ...
muda os tempos de arquivos mesmo se o fluxo não for alterado.-i
viola a filosofia do Unix ( mywiki.wooledge .org / BashFAQ / 021 # Using_nonstandard_tools ). Se você quiser evitar isso, useex
(por exemplo,ex -sc '%s/FOO/BAR/ge|x' /Users/gjtorikian/blah
).