Pesquisa recursiva e substituição em arquivos de texto no Mac e Linux

127

No shell linux, o comando a seguir procurará e substituirá recursivamente todas as instâncias de 'this' por 'that' (não tenho um shell Linux na minha frente, mas deve).

find . -name "*.txt" -print | xargs sed -i 's/this/that/g'

Como será um comando semelhante no OSX?

Jack
fonte
Provavelmente deve ser movido para, apple.stackexchange.compois não é genérico o suficiente para linux nem todos os desenvolvedores.
AlikElzin-kilaka

Respostas:

246

O OS X usa uma combinação de ferramentas BSD e GNU, portanto, é melhor sempre verificar a documentação (apesar de eu ter uma que lessnem estava em conformidade com a página de manual do OS X):

https://web.archive.org/web/20170808213955/https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/sed.1.html

sed usa o argumento depois -icomo extensão para backups. Forneça uma string vazia ( -i '') para nenhum backup.

O seguinte deve fazer:

LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +

A -type fé apenas uma boa prática; O sed reclamará se você fornecer um diretório ou algo assim. -execé preferido sobre xargs; você não precisa se preocupar com -print0nada. O {} +no final significa que findanexará todos os resultados como argumentos a uma instância do comando chamado, em vez de executá-lo novamente para cada resultado. (Uma exceção é quando o número máximo de argumentos de linha de comando permitidos pelo sistema operacional é violado; nesse caso, findserá executado mais de uma instância.)

TaylanUB
fonte
2
Meu "this" nesta substituição contém uma barra (localhost / site) - estou substituindo partes de uma URL em um arquivo .html .... como faço para fazer essa substituição. Tentei colocar aspas duplas, mas falha.
Timothy T.
6
Sintaxe sed permite usar quase qualquer personagem no lugar da barra, por exemplo, você poderia usar o %caráter: sed "s%localhost/site%blah/blah%". Outra alternativa é a barra invertida escapar do separador: sed "s/localhost\/site/blah\/blah/".
TaylanUB
Obrigado, deixe-me tentar isso. I no entanto fez tentar usar {} para separar a barra e eu ainda tenho um erro ...
Timothy T.
16
Mais alguém está recebendo illegal byte sequenceerro? se assim for, tente LC_ALL=C find . -type f -name '*.txt' -exec sed -i '' s/this/that/ {} +:, funcionou para mim.
ColdTuna
10
Isto vai substituir apenas uma ocurreny por arquivo, uso /gde múltiplas ocurrencies comoLC_ALL=C find . -type f -exec sed -i '' s/search/replace/g {} +
jamesjara
152

Para o mac, uma abordagem mais semelhante seria a seguinte:

find . -name '*.txt' -print0 | xargs -0 sed -i "" "s/form/forms/g"
Guybrush Threepwood
fonte
10
Eu gostaria de poder votar isso toda vez que voltar a usá-lo. Seria às +15 agora, fácil.
Droogans
Por alguma razão, isso não funciona para mim. Não faz nada. Eu estou dentro do form360 pasta e estou tentando mudar todas as instâncias de string com o nome EASYFORM para form360, estou executando o seguinte comando:find . -name '*.php' -print0 | xargs -0 sed -i "" "s/easyform/form360/g"
Andres Ramos
2
para mim, essa deve ser a resposta correta. É o único que funcionou para mim.
nosequeldeebee
1
-print0 | xargs -0 não funciona no meu mac quando o nome do arquivo contém espaço.
user2807219
1
. sed:: Edição no local só funciona para arquivos regulares
codeslapper
14

Como solução alternativa, estou usando esta no Mac OSX 10.7.5

grep -ilr 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @

O crédito vai para: resposta de Todd Cesere

Maciej Gurban
fonte
Este funciona bem! Outros scripts adicionam um fim de linha extra em alguns casos no OSX! Muito obrigado!
Sebastien Filion
14

No Mac OSX 10.11.5, isso funciona bem:

grep -rli 'old-word' * | xargs -I@ sed -i '' 's/old-word/new-word/g' @
madx
fonte
xargs -I @ sed -i '' s / old-word / new-word / g '@ funcionou para mim depois de tentar tantas outras sugestões que não funcionavam. Obrigado!
DrB
13

Nenhuma das opções acima funciona no OSX.

Faça o seguinte:

perl -pi -w -e 's/SEARCH_FOR/REPLACE_WITH/g;' *.txt
eb80
fonte
1
como escapar '/' se SEARCH_FOR e REPLACE_WITH forem caminhos?
rraallvv
Use um delimitador diferente. Se você estiver usando caminhos, dois pontos ou cano funcionariam. 's | SEARCH | REPLACE | g', por exemplo. Nosso uso de chaves, como em {SEARCH} {REPLACE} '.
Dannysauer 6/09/13
1
pergunta, tentando não no Mac - mas parece gerar um erro? Por exemplo, meu caminho é interpretado como um arquivo? -bash: localhost / NoHost: Nenhum tal lima ou diretório
Timothy T.
isso não ocorre através de pastas profundas. Apenas um nível.
Sergey Romanov
Para obter mais informações sobre este comando, leia este link lifehacker.com/5810026/…
kuzdu
7

Uma versão que funciona no Linux e no Mac OS X (adicionando a -eopção a sed):

export LC_CTYPE=C LANG=C
find . -name '*.txt' -print0 | xargs -0 sed -i -e 's/this/that/g'
vroc
fonte
Eu tive que fazer a exportação a partir desta resposta + a linha da resposta aceita (eu não queria que os arquivos de backup a ser gerado)
Lance
2
para corrigir o erro 'sequência ilegal de bytes', tente configurar o LOCALE antes de executar o comando: export LC_CTYPE=C && export LANG=C
cjoy
NUNCA EXECUTE isso com '*' em vez de '* .filetype' como eu fiz se estiver usando o Git. Ou você pode dizer adeus a todo o seu trabalho não publicado.
Sergey
1
A versão para Mac do comando sed requer uma '' após o -i, assim que esta resposta está incorreta
G Huxley
2

Sempre que digito esse comando, sempre pareço manejá-lo ou esquecer uma bandeira. Criei um Gist no github baseado na resposta do TaylanUB que substitui uma descoberta global do diretório atual. Isso é específico do Mac OSX.

https://gist.github.com/nateflink/9056302

É legal porque agora eu apenas abro um terminal e copio:

curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

Você pode obter alguns erros estranhos na sequência de bytes, então aqui está o código completo:

#!/bin/bash
#By Nate Flink

#Invoke on the terminal like this
#curl -s https://gist.github.com/nateflink/9056302/raw/findreplaceosx.sh | bash -s "find-a-url.com" "replace-a-url.com"

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Usage: ./$0 [find string] [replace string]"
  exit 1
fi

FIND=$1
REPLACE=$2

#needed for byte sequence error in ascii to utf conversion on OSX
export LC_CTYPE=C;
export LANG=C;

#sed -i "" is needed by the osx version of sed (instead of sed -i)
find . -type f -exec sed -i "" "s|${FIND}|${REPLACE}|g" {} +
exit 0
Nate Flink
fonte
2

Este é o meu viável. no mac OS X 10.10.4

grep -e 'this' -rl . | xargs sed -i '' 's/this/that/g'

Os itens acima usam find alterarão os arquivos que não contêm o texto de pesquisa (adicione uma nova linha no final do arquivo), que é detalhada.

NoteCode
fonte
2

Se você estiver usando um terminal zsh, poderá usar magia curinga:

sed -i "" "s/search/high-replace/g" *.txt

Mentor
fonte
1

Usei esse formato - mas ... descobri que precisava executá-lo três ou mais vezes para que ele realmente alterasse todas as instâncias que eu achava extremamente estranhas. A execução uma vez alteraria algumas em cada arquivo, mas não todas. Executar exatamente a mesma string duas a quatro vezes capturaria todas as instâncias.

find . -type f -name '*.txt' -exec sed -i '' s/thistext/newtext/ {} +
Emma
fonte
Você precisava executar esse comando várias vezes gporque o seu regex sed precisa de um no final, caso contrário, ele substitui apenas a primeira ocorrência de thistextuma linha. Portanto, o seu regex deve sers/thistext/newtext/g
Les Nightingill
0

https://bitbucket.org/masonicboom/serp é um utilitário go (ou seja, multiplataforma), testado no OSX, que pesquisa e substitui recursivamente o texto em arquivos em um determinado diretório e confirma cada substituição. É novo, então pode ser de buggy.

O uso se parece com:

$ ls test
a  d  d2 z
$ cat test/z
hi
$ ./serp --root test --search hi --replace bye --pattern "*"                         
test/z: replace hi with bye? (y/[n]) y
$ cat test/z
bye
user4425237
fonte
0
find . -type f | xargs sed -i '' 's/string1/string2/g'

Consulte aqui para mais informações.

user2725109
fonte
-3

O comando no OSX deve ser exatamente o mesmo que é o Unix sob a bonita interface do usuário.

clops
fonte
Eu pensei isso, mas não. Ele coloca um ./ na frente dos arquivos ao alimentar o sed, portanto o sed reclama 'código de comando inválido'. Talvez eu só estou cansado ;-)
Jack
@ JacobusR, recorte e cole no seu terminal OSX - você deve inserir algo um pouco diferente.
Glenn Jackman
3
Você pode querer -print0adicionar aos findsinalizadores e -0aos xargssinalizadores.
jmtd
Você também pode simplesmente usar em -exec sed -i s/this/that/g {} \+vez de -printe xargs
Marian
@ JacobusR - isso funciona, mesmo se houver um nome de caminho './' principal. No entanto, se houver um espaço no nome do arquivo, diga "./jacobus \ R.txt", o uso de xargspoderá ver o "./jacobus" delimitado por espaço como um arquivo para o qual passar e sed, em seguida, com "R.txt" , nenhum dos quais existe.
Brett Hale
-4

poderia apenas dizer $ PWD em vez de "."

Samuel Devlin
fonte