Maneira simples de localizar arquivos, aplicar mudanças e gravar em outro diretório sob nomes diferentes?

1

Eu escrevi algum script foo que aceita um caminho de arquivo, lê o arquivo, aplica algumas alterações nele e envia o arquivo alterado para stdout:

foo src/file.foo > dest/file.changed.foo # works fine
cat src/file.foo | foo > dest/file.changed.foo # also works fine

Agora, quero aplicar esse comando a vários arquivos em um diretório e, para cada arquivo:

src/file.foo --> foo --> dest/file.changed.foo

Com base na resposta a um pergunta semelhante Eu fiz o seguinte:

find src -name '*.foo' \
  -exec bash -c 'for x; do dest=${x/src/dest}; foo ${x} > ${dest/\.foo$/.changed.foo}; done' _ {} +

Os trabalhos acima, mas o problema é que é muito complicado para o uso diário. Não existe uma maneira mais simples de fazer isso? Eu queria saber se é a maneira que o foo é feito que complica as coisas.

    
por Oleg 14.08.2016 / 17:32

3 respostas

2

É muito menos digitação e mexer se você usa globalização recursiva. No bash, coloque shopt -s globstar no seu .bashrc . No ksh93, a globalização recursiva requer set -o globstar ; em zsh funciona fora da caixa. Esteja ciente de que, no bash, a globalização recursiva também recorre sob links simbólicos para diretórios.

Para salvar a manipulação de strings, primeiro mude para o topo da árvore de fontes (ou para o topo da árvore de destino).

cd foo
for x in **/*.foo; do foo "$x" >"../dest/${x%.*}.changed.foo"; done

Você pode omitir as aspas duplas se souber que seus nomes de arquivo não contêm caracteres em branco ou globbing.

No zsh, as aspas duplas nunca são necessárias, e você pode economizar um pouco mais de digitação mesmo sem alterar o diretório atual.

for x in src/**/*.foo; do foo $x >../dest/${${x#*/}%.*}.changed.foo; done
for x in src/**/*.foo; do foo $x >../dest/${x#*/}:r.changed.foo; done
for x (src/**/*.foo) foo $x >../dest/${x#*/}:r.changed.foo

Se você fizer isso com frequência, deverá definir uma regra de criação, por exemplo, em um arquivo GNUmake (use uma aba onde usei 8 espaços):

source_files = $(shell find src -name '*.foo')
destination_files = $(patsubst src/,dest/,$($(source_files)%.foo=.changed.foo))

default: all-foo
all-foo: $(destination_files)

dest/%.changed.foo: src/%.foo
        foo $< >$@
    
por 15.08.2016 / 00:02
1

Supondo que seus nomes de arquivos não tenham novas linhas incorporadas:

find src -name '*.foo' -type f | \
    while IFS= read -r sf; do
        df=dest/${sf##./src/} && \
        mkdir -p "$(dirname "$df")" && \
        foo "$sf" >"${df%%.foo}.changed.foo"
    done
    
por 14.08.2016 / 17:47
1

Supondo que você não tenha uma árvore de arquivos de origem (por exemplo, não src/dir1/a.foo ; todos eles estão diretamente no diretório src ), não será necessário o wrapper find e faça isso diretamente concha:

for x in src/*.foo
do
  dest="${x/src/dest}"
  foo "$x" > "${dest/%.foo/.changed.foo}"
done

A complicação, aqui, é que você quer mudar nomes de arquivos, e não é uma conseqüência de foo .

Podemos facilitar um pouco se foo não precisar ser executado no diretório atual

cd src
for x in *.foo
do
  foo "$x" > "../dest/${x/%.foo/.changed.foo}"
done

Se você não quiser que a extensão seja alterada, é o loop mais simples possível

cd src
for x in *.foo
do
  foo "$x" > "../dest/$x"
done

Todos esses loops podem ser alterados em uma única linha pelo uso estratégico do caractere ; . por exemplo, o primeiro loop torna-se

for x in src/*.foo; do dest="${x/src/dest}" ; foo "$x" > "${dest/%.foo/.changed.foo}" ; done
    
por 14.08.2016 / 17:47

Tags