Execute um comando com curingas em cada subdiretório

2

Eu uso este comando para fazer uma montagem de todas as imagens em um diretório:

gmic *jpg -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o -0000."$(date)".jpg

Eu quero executar este comando em um diretório e seus subdiretórios recursivamente. Então, em cada diretório eu criaria uma montagem das imagens nesse diretório.

Eu tentei:

find -exec gmic *jpg -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o -0000."$(date)".jpg

mas deu-me o erro

find: missing argument to '-exec'

Eu também tentei

find -exec gmic -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o -0000."$(date)".jpg {} \;

que me deu o seguinte erro:

[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Output image [] as file '-0000.Fri Mar 13 04:33:44 EDT 2015.jpg', with quality 100%.
[gmic]-0./ *** Error *** Command '-output': File '-0000.Fri Mar 13 04:33:44 EDT 2015.jpg', instance list (0,(nil)) is empty.
[gmic] *** Error in ./ *** Command '-output': File '-0000.Fri Mar 13 04:33:44 EDT 2015.jpg', instance list (0,(nil)) is empty.
    
por user8547 12.03.2015 / 17:04

2 respostas

3

Ok, você quer executar um comando em cada diretório em uma árvore de diretório - o diretório atual, seus subdiretórios, seus subdiretórios, etc. A primeira coisa a fazer é enumerar os diretórios em questão. Com o comando find , diga para listar apenas os diretórios:

find . -type d

O comando que você deseja executar em cada diretório é

gmic ./*jpg -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o -0000."$(date)".jpg

Este é um comando shell, contendo uma expansão curinga e uma substituição de comando. Você precisa executar um shell para executá-lo. Como esse shell será informado sobre o que fazer por find , não será o shell em que você está executando find in: é necessário informar find para executar um shell. Passe o diretório onde você quer agir como um argumento para o shell.

find . -type d -exec sh -c '…' {} \;

Antes de poder chamar o gmic , você precisa fazer algumas coisas nesse script: altere para o diretório em questão e verifique se ele contém .jpg arquivos.

find . -type d -exec sh -c '
  cd "$0" || exit
  set -- *.jpg
  if [ -e "$1" ]; then
    gmic "$@" -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o -0000."$(date)".jpg
  fi
' {} \;

Como alternativa, você pode dizer a find para listar .jpg arquivos. No entanto, isso dificulta a execução do comando apenas um por diretório.

Se o seu shell for bash, ao contrário de sh simples, você pode usar seu curinga ** para recorrer aos diretórios. Isso é mais fácil do que usar find . No bash ≤4.2, cuidado com o fato de ** atravessar o link simbólico para os diretórios. Você pode simplificar um pouco o teste de existência de arquivos.

shopt -s globstar nullglob
for dir in ./**/*/; do
  files=("$dir/"*.jpg)
  if [[ ${#files[@]} -ne 0 ]]; then
    gmic "${files[@]}" -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o "$dir/-0000.$(date)".jpg
  fi
done

No zsh, você pode usar um modificador de histórico em um qualificador de glob para enumerar os diretórios que contêm .jpg arquivos e, em seguida, filtrar a matriz resultante para manter uma única cópia de cada diretório .

dirs=(./**/*.jpg(:h))
for dir in ${(u)dirs}; do
  gmic $dir/*.jpg -gimp_montage 4,\""V(H(0,1),H(2,V(3,4)))"\",1,1.0,0,5,0,0,0,255,0,0,0,0 -o "$dir/-0000.$(date).jpg"
done

(Aside: -0000.Sat Mar 14 15:51:28 CET 2015.jpg é um nome muito estranho para um arquivo. Arquivos cujo nome começa com - tendem a causar problemas porque se parecem com opções de comandos. Datas são mais convenientes para manipular em um formato onde ordenar lexicograficamente é idêntico a ordenar cronologicamente, como 20150314-145128 .)

    
por 14.03.2015 / 15:52
0

Como no resto dos comentários, não sei ao certo o que você precisaria de algo para permitir que você execute qualquer comando mais do que -exec ou piping bits em um while ou loop. A opção exec permitirá que você use "{}" no lugar do que encontrou (caminho completo, não relativo). Aqui está um exemplo rápido da opção -exec e do loop for.

$ tree
.
|-- a
|   |-- a
|   |   '-- a
|   '-- b
'-- b
    |-- a
    '-- b

7 directories, 0 files
$ find . -type d -exec touch {}/foo \
$ tree
.
|-- a
|   |-- a
|   |   |-- a
|   |   |   '-- foo
|   |   '-- foo
|   |-- b
|   |   '-- foo
|   '-- foo
|-- b
|   |-- a
|   |   '-- foo
|   |-- b
|   |   '-- foo
|   '-- foo
'-- foo

7 directories, 8 files
$ for i in $(find . -type f); do mv $i $i.moved; done
$ tree
.
|-- a
|   |-- a
|   |   |-- a
|   |   |   '-- foo.moved
|   |   '-- foo.moved
|   |-- b
|   |   '-- foo.moved
|   '-- foo.moved
|-- b
|   |-- a
|   |   '-- foo.moved
|   |-- b
|   |   '-- foo.moved
|   '-- foo.moved
'-- foo.moved

7 directories, 8 files
    
por 12.03.2015 / 19:29