Como executar esse tipo de comando a partir de "find"?

2

Estou usando o FreeBSD e tendo dificuldades com comandos que incluem {} como parte de uma string. Por exemplo, ao renomear os arquivos encontrados. Note que este é um exemplo da situação. A questão em si é sobre como resolver os problemas da sintaxe "{}" em geral:

find . -type f -name 'data*' -execdir mv {} OLD_{} \;
find . -type f -name 'data*' -execdir mv {} archive/{} \;

Observe que -execdir evita problemas com o caminho do diretório contido, executando o comando a partir do diretório contido e expandindo {} para apenas o nome do arquivo.

Existem dois problemas:

  1. Como citar corretamente os argumentos na cláusula -execdir mv (muitos arquivos terão espaços ou aspas simples em seus nomes).
  2. Como fazer com que o alvo substitua o nome do arquivo.

O segundo problema surge porque {} para "caminho do item encontrado" só parece ser expandido se estiver cercado por espaços iniciais / finais, o que atrapalha o comando args. Exemplo com caracteres iniciais e finais não espaciais:

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):" {}  \;
(result): .
(result): dir 1
(result): dir 2
(result): dir 3

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{}  \;
(result):
(result):
(result):
(result):

# /usr/bin/find . -maxdepth 1 -execdir echo {}":(result)" \;
:(result)
:(result)
:(result)
:(result)

man find declara que "Implementações históricas das primárias -exec e -ok não substituíram a sequência" {} "no nome do utilitário ou os argumentos do utilitário se ele tinha caracteres anteriores ou posteriores a espaços em branco. Esta versão substitui, não importa onde no nome do utilitário ou argumentos aparece ", mas isso não parece estar acontecendo.

Como posso executar o comando que quero executar?

    
por Stilez 03.07.2018 / 05:07

3 respostas

2

Este comportamento não é reproduzível com find no FreeBSD 11.1-RELEASE quando usar /bin/sh como o shell. Eu era capaz de reproduzi-lo sob /bin/csh e /bin/tcsh .

Para corrigir isso em csh e tcsh , cite {} as \{\} ou '{}' ou use o método abaixo.

Para concatenar corretamente o nome do caminho atual com alguma outra string em uma implementação de find que não expanda {} corretamente ao usar como parte de uma string, pode-se fazer isso com um shell filho.

Exemplo:

find . -type f -name '*.c' -exec sh -c 'printf "(result):%s\n" "$@"' sh {} +

ou, com echo (mas veja " Por que printf é melhor que echo? "),

find . -type f -name '*.c' -exec sh -c '
    for name do
        echo "(result):$name"
    done' sh {} +

ou

find . -type f -name '*.c' -execdir sh -c '
    for name do
        mv -- "$name" "OLD_${name##*/}"
    done' sh {} +

Ou seja, dê ao shell filho ( sh -c here) os nomes de caminho como comando argumentos de linha e, em seguida, usá-los no shell gerado para concatenar como normalmente usaria variáveis de shell.

(o ${name##*/} acima é apenas para proteger contra o GNU find que pré-acrescenta ./ aos nomes de caminho ao usar -execdir )

Relacionados:

por 03.07.2018 / 12:25
0

Eu estava confuso sobre os dois últimos exemplos. Aqui, se find não substituísse o {} , então alguém poderia supor que ele ficaria como está e a saída seria (result):{} :

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{}  \;
(result):

Talvez seja o seu shell que quebra as foo{} strings em vez disso?

O tcsh parece ter feito exatamente isso:

$ tcsh -c 'echo foo{}bar '
foobar
$ tcsh -c 'echo foo {} bar '
foo {} bar

Isso foi também mencionado nos comentários em outra pergunta sobre como citar {} , GNU encontra e mascara o {} para alguns shells - qual? .

Solução alternativa: coloque aspas nas strings que contêm {} :

$ tcsh -c 'echo "foo{}bar" '
foo{}bar

(Outro shell que precisa ser citado para {} é fish , mas também descartaria o lone {} .)

    
por 04.07.2018 / 21:32
0

Você pode estar melhor usando algo como:

find . -type f -name 'data*' -print0 | xargs -0 -I % mv '%' 'OLD_%'

usando find -print0 | xargs -0, cada nome de arquivo é separado por um byte nulo, portanto, espaços e outros caracteres especiais no nome do arquivo não resultarão na divisão do nome do arquivo em dois argumentos separados.

    
por 06.09.2018 / 02:31

Tags