$ tree
.
'-- MainDir
|-- dir1
| |-- notes.txt
| |-- some_file-1
| |-- some_file-2
| '-- some_file-3
'-- dir17
|-- README.txt
|-- some_file-1
|-- some_file-2
'-- some_file-3
3 directories, 8 files
Usando rsync :
$ rsync -a --include '*.txt' --exclude '*/*' MainDir/ MainDir/txtFiles/
$ tree
.
'-- MainDir
|-- dir1
| |-- notes.txt
| |-- some_file-1
| |-- some_file-2
| '-- some_file-3
|-- dir17
| |-- README.txt
| |-- some_file-1
| |-- some_file-2
| '-- some_file-3
'-- txtFiles
|-- dir1
| '-- notes.txt
'-- dir17
'-- README.txt
6 directories, 10 files
O comando usa --include '*.txt' para incluir todos os arquivos com .txt nome do arquivo e --exclude '*/*' para excluir outros arquivos. Usando -a ou --archive , uma cópia da parte da hierarquia de origem que é copiada é criada no destino.
Executar isso novamente resultaria em um diretório MainDir/txtFiles/txtFiles vazio sendo criado. Adicionar -m ou --prune-empty-dirs à invocação de rsync evitaria isso.
Usando find (de MainDir ):
$ find . -type f -name '*.txt' ! -path '*/txtFiles/*' \
-exec sh -c 'for n; do
d="txtFiles/${n%/*}";
mkdir -p "$d" && cp "$n" "$d"
done' sh {} +
Isso localiza todos os arquivos regulares cujos nomes correspondem a *.txt e que não estão em um diretório txtFiles .
Para cada caminho de nome de arquivo encontrado, a parte do diretório do caminho é criada em txtFiles e o arquivo é copiado lá.
A única diferença entre a abordagem rsync e isso é que rsync pode copiar arquivos não regulares que correspondem ao nome dado (diretórios chamados pipes etc.) e que o comando find deve ser executado de dentro de MainDir .