
Com o tempo, encontrei o mesmo padrão repetidamente: tenho algum tipo de estrutura de diretórios:
example/
├── a
│ └── c
│ ├── d.txt (120k)
│ └── e.txt (60k)
└── b
└── f.txt (280k)
E eu quero "copiar" os arquivos para outro diretório, digamos, example_grepped
, aplicando um comando a cada um como se estivesse no lugar de cp
- digamos, grep ERROR
digamos, acabo com uma pasta com a mesma estrutura, mas com arquivos filtrados pelo grep
.
example_grepped/
├── a
│ └── c
│ ├── d.txt (1k)
│ └── e.txt (0b)
└── b
└── f.txt (12k)
O mesmo padrão para converter arquivos de mídia (FLACs em MP3s, PNGs em JPGs) e, desta vez, ao converter diferentes formatos de esquema como parte de um processo de construção.
Existe um comando genérico que eu possa usar? Algo como foobar example example_grepped --command 'grep ERROR'
ou foobar flacs mp3s --command 'ffmpeg -i {} {}.mp3'
?
Uma bandeira obscura xargs
, talvez? (um find
canalizado xargs
seriaquaseé suficiente, mas a maioria, senão todos os comandos, esperam que a estrutura de diretórios já exista.)
Responder1
A resposta mais próxima que posso encontrar sem recriar separadamente a estrutura de diretórios é usarinstalar:
cd example
find . -type f -exec sh -c 'grep ERROR {} | install -D /dev/stdin /tmp/example_grepped/{}' \;
Infelizmente, o procedimento acima só funcionará se o seu comando puder enviar o resultado para STDOUT.
Responder2
Outra maneira de abordar isso é usar um programa que faça cópias recursivas de qualquer maneira. Eu verifiquei rsync
, mas não consegui encontrar uma opção de retorno de chamada rapidamente. Mas o gnu tar
tem uma opção --to-command
para a qual você pode fornecer um comando a ser executado que obtém a entrada do arquivo stdin
. Mas como criar o arquivo então? Bem, o comando chamado encontra o nome do arquivo atual em $TAR_FILENAME
.
Juntando tudo, a chamada básica é
tar cf - example | tar xf - --to-command="./script example_grepped 'grep-pattern'"
onde o script poderia ser algo como
#!/bin/bash
mkdir -p $(dirname "$1/$TAR_FILENAME")
grep '$2' >"$1/$TAR_FILENAME"
exit 0
Outra maneira de abordar isso seria envolver o tubo tar em um script que faz com que o comando seja executado na linha de comando. No entanto, a fuga para a mkdir ...dirname
construção será um pouco desafiadora.
Responder3
#!/bin/bash
filter() {
local target_root="${@: -1}"
target_path=$(sed -E "s/[^/]*/$target_root/" <<< "$1")
target_dir=$(dirname "$target_path")
mkdir -p "$target_dir"
if [[ -f $1 ]]; then
# do your grep thing here
grep burger "$1" > "$target_path"
fi
}
export -f filter
source_root="example"
target_root="example_grepped"
find "$source_root/" -print0 | xargs -0 -I content bash -c "filter 'content' '$target_root'"
Este script também funciona com nomes de diretórios e arquivos que contêm espaços.
Execute este script onde o diretório de origem ("exemplo") está localizado.
Responder4
Usando o GNU Parallel você pode fazer algo assim:
cd src
find . -type f | parallel 'mkdir -p ../dst/{//}; dostuff --input {} --output ../dst/{}'