1
Para sua primeira etapa, no seu exemplo de comando, você não precisa do *
porque ele não está filtrando nenhum arquivo. O que você precisa é filtrar .git
, como este:
$ find . -name '.git' -prune -o -type f -print
Isso rejeitará ( -prune
) qualquer diretório com o mesmo nome .git
e tudo dentro dele.
Isso elimina a necessidade de grep -v ".git"
. O sed não poderia ser evitado, já que está editando os componentes internos de um arquivo, algo que a localização não poderia fazer.
Mas ainda assim, podemos simplificar (citando o ponto dentro do sed):
$ find . -name '.git' -prune -o -type f -exec sed -i 's/com\.foo/org\.bar/g' '{}' \+
Isso acumulará nomes de arquivos (semelhantes a xargs) ao comando sed
.
Mas ainda melhor:
$ find . -name '.git' -prune -o -type f -execdir sed -i 's/com\.foo/org\.bar/g' '{}' \+
Que executará uma instância de sed por diretório.
2 e 3
Vamos definir uma função para executar a renomeação de dir:
$ renamedir (){ find "$1" -name '.git' -prune -o -type d -exec bash -c 'mv "$1" "${1//"$2"/$3}"' sh '{}' "$2" "$3" \; ; }
Etapa 2:
-
para todos os diretórios (excluindo .git) em src / com renomear para src / org
$ renamedir 'd/src/com' 'src/com' 'src/org'
Etapa 3:
-
para todos os diretórios (excluindo .git) em src / org / foo renomear para src / org / bar
$ renamedir 'src/org/foo' 'ogr/foo' 'org/bar'
4
Etapa 4:
-
para todos os nomes de arquivos e diretórios (excluindo .git) com "com.foo" no nome, altere essa parte para "org.bar".
$ find . -name '.git' -prune -o -name "com.foo" -type f -exec bash -c 'mv "$1" "${1//"$2"/$3}"' sh '{}' "com.foo" "org.bar" \; ; }
Tudo dentro de um script (melhor formatado):
#!/bin/bash
# NOTE: There are three echo to avoid execution of commands.
# If after testing, you decide that it is ok to execute commands,
# remove the echo words.
# Step 1:
### for all files (excluding .git) in . change
### file contents from com.foo to org.bar
find . -name '.git' -prune -o -type f -execdir echo sed -i 's/com\.foo/org\.bar/g' '{}' \+
renamedir (){ find "$1" -name '.git' -prune -o -type d -exec bash -c '
echo \
mv "$1" "${1//"$2"/$3}"
' sh '{}' "$2" "$3" \;
}
# Step 2:
### for all directories (excluding .git) in src/com rename to src/org
renamedir 'd/src/com' 'src/com' 'src/org'
# Step 3:
### for all directories (excluding .git) in src/org/foo rename to src/org/bar
renamedir 'src/org/foo' 'org/foo' 'org/bar'
# Step 4:
### for all file and directory names (excluding .git)
### with "com.foo" in the name, change that portion to "org.bar".
find . -name '.git' -prune -o -name "*com.foo*" -type f -exec bash -c '
echo \
mv "$1" "${1//"$2"/$3}"
' sh '{}' "com.foo" "org.bar" \; ; }
Para testar as diferentes versões de correspondência, crie uma árvore inteira de arquivos como esta (remova-a depois com rm -rf ./d/
):
$ mkdir -p d/src/{{,.git}{,one},com/{{,.git}{,two},\ .git,.git/six},org/{{,.git}{,three},bar/{,.git}{,four},foo/{,.git}{,five}}}
$ touch d/src/{111,com/{222,.git/666},org/{333,bar/444,foo/555}}
$ touch d/src/{.git/{aaa,one/aaaaa},one/aaa111,com/.git{/bbb,two/bbb222},org/{.git{/ccc,three/ccc333},bar/.git{/ddd,four/ddd444},foo/.git{/eee,five/eee555}}}
Seu comando original (usando a opção GNU -z para encontrar find -print0) irá corresponder (neste diretório):
$ find d/ -type f -print0 | grep -zZv \.git
find d/ -type f -print0 | grep -zZv \.git | xargs -0 printf '<%s>\n'
<d/src/org/333>
<d/src/org/foo/555>
<d/src/org/bar/444>
<d/src/com/222>
<d/src/111>
Apenas cinco arquivos, aqueles que não têm relação com nada .git
.
Em geral:
Existem três maneiras principais de corresponder .git
em find
: -name
, -regex
e -path
.
Deixando de lado -regex
que além de ser ímpar (usa emacs regex) é necessário apenas para combinações complexas que aqui não são necessárias.
Um find d/ -path "./.git"
corresponderá exatamente a isso: somente um diretório .git
de inmediate.
Ou (para o diretório criado) find . -path './d/src/.git'
corresponderá apenas a ./d/src/.git
Um find d/ -name ".git"
corresponderá exatamente a .git
no último nível (basename) de uma árvore.
Tanto -name "*.git"
quanto -path "*.git"
serão exatamente iguais.
Qualquer string que termine em .git
.
O que começa a ficar complexo é quando adicionamos dois asteriscos: *.git*
Nesse caso, -name
corresponderá apenas ao último nível do caminho (o nome base do arquivo), mas devido aos dois asteriscos *
também corresponderá aos nomes com prefixo (observe o espaço em .git
), e postfix (note muitos como .gitone
):
$ find d -name '*.git*'
d/src/org/.gitthree
d/src/org/foo/.git
d/src/org/foo/.gitfive
d/src/org/bar/.gitfour
d/src/org/bar/.git
d/src/org/.git
d/src/com/.gittwo
d/src/com/.git
d/src/com/ .git
d/src/.git
d/src/.gitone
corresponderá a .git
em qualquer nível do caminho, quase como grep funciona. Não é exatamente o mesmo que grep, pois o grep poderia ter um Regex complexo, mas aqui a correspondência é contra um simples "padrão". Observe o d/src/com/.git/six
abaixo:
$ find d -path '*.git*'
d/src/org/.gitthree
d/src/org/foo/.git
d/src/org/foo/.gitfive
d/src/org/bar/.gitfour
d/src/org/bar/.git
d/src/org/.git
d/src/com/.gittwo
d/src/com/.git
d/src/com/.git/six
d/src/com/ .git
d/src/.git
d/src/.gitone
Em seguida, podemos modificar essas correspondências de várias maneiras, usando ( not
ou !
), que rejeitará o que é seguido pelo seguinte:
Não (!)
Se find d -name '.git'
corresponder:
d/src/org/foo/.git
d/src/org/bar/.git
d/src/org/.git
d/src/com/.git
d/src/.git
Em seguida, find d -not -name '.git'
(ou exatamente o equivalente: find d ! -name '.git'
) corresponderá a todos os outros.
Não é impresso aqui, pois a lista é bem longa.
Mas selecionar apenas arquivos é curto (observe o arquivo 666
):
$ find d -not -name '.git' -type f
d/src/org/333
d/src/org/foo/555
d/src/org/bar/444
d/src/com/222
d/src/com/.git/666
d/src/111
E -path
em find d -not -path '*.git*' -type f
corresponderá a apenas cinco arquivos:
$ find d -not -path '*.git*' -type f
d/src/org/333
d/src/org/foo/555
d/src/org/bar/444
d/src/com/222
d/src/111
Ameixa
Ou podemos usar -prune
para "cortar" ou "remover" todos os diretórios que o anterior corresponde aceito:
$ find d \( -name '.git' -prune \) -o \( -print \)
d
d/src
d/src/org
d/src/org/three
d/src/org/333
d/src/org/.gitthree
d/src/org/foo
d/src/org/foo/five
d/src/org/foo/555
d/src/org/foo/.gitfive
d/src/org/bar
d/src/org/bar/.gitfour
d/src/org/bar/four
d/src/org/bar/444
d/src/one
d/src/com
d/src/com/.gittwo
d/src/com/222
d/src/com/two
d/src/com/ .git
d/src/111
d/src/.gitone
Deve haver algo depois de -prune. Normalmente, um '-o' e aqui também um -print
para corresponder a tudo o que a remoção não correspondeu. Como você pode ver, isso combina muito mais que os cinco arquivos do seu comando.
A alteração de '.git'
para '*.git'
removerá apenas d/src/com/ .git
.
Mas usar '*.git*'
funcionará mais perto do grep (não exatamente o mesmo):
$ find d \( -name '*.git*' -prune \) -o \( -print \)
d
d/src
d/src/org
d/src/org/three
d/src/org/333
d/src/org/foo
d/src/org/foo/five
d/src/org/foo/555
d/src/org/bar
d/src/org/bar/four
d/src/org/bar/444
d/src/one
d/src/com
d/src/com/222
d/src/com/two
d/src/111
Isto irá corresponder apenas aos cinco arquivos (não diretórios) do seu comando:
$ find d \( -name '*.git*' -prune \) -o \( -type f -print \)
Como também isso irá corresponder apenas aos cinco arquivos iniciais. Adicionar -type f
oculta muitos detalhes:
$ find d \( -name '.git' -prune \) -o \( -type f -print \)
Geralmente sem parênteses (menos claro):
$ find d -name '.git' -prune -o -type f -print
Aviso: a remoção de -print
pode ter efeitos colaterais indesejados.