find . -iname "*.extension" -exec sh -c '
exec <command> "$@" <additional parameters>' sh {} +
Disclaimer: Eu sou um novato para Unix / Linux, mas estou ansioso para aprender! Eu tentei uma pesquisa neste stackexchange e li o man find
, mas não consigo descobrir isso.
Eu quero usar o comando find ... -exec {} +
para encontrar recursivamente todos os arquivos com uma extensão de arquivo específica e executar um comando na lista de arquivos. Existem aproximadamente 100k arquivos que eu preciso converter. O comando que estou executando aceita o nome do arquivo (ou uma lista de nomes de arquivos, por exemplo, f1 f2 f3
) como um parâmetro, mas também preciso especificar parâmetros adicionais para executar o comando.
O que eu tentei até agora:
Isso funciona:
find . -iname "*.extension" -exec <command> {} <additional parameters> \;
Isso não parece funcionar:
find . -iname "*.extension" -exec <command> {} <additional parameters> +
Eu recebo a mensagem de erro, find: missing argument to '-exec'
. Eu estou supondo que não posso especificar parâmetros adicionais após o {}
Algumas notas:
O comando em questão leva o nome do arquivo como o primeiro parâmetro e, em seguida, preciso designar alguns parâmetros adicionais, como o diretório de saída -o <outputDir>
e as variáveis a serem extraídas dos arquivos -v <var1,var2,...>
.
Estou executando isso no terminal no Ubuntu 12.04, se isso faz alguma diferença.
Com o +
, ele listará vários nomes de arquivos separados por espaços no lugar de {}
(e será uma lista longa, pois você tem 100000 arquivos) em vez de apenas um único nome de arquivo. Sendo esse o caso, o {}
é necessário para chegar no final do comando.
Veja a página find(1)
man em -exec command {} +
.
Supondo que todos os diretórios e arquivos tenham nomes regulares, ou seja, sem espaços, sem linhas ou similares, isso deve funcionar mesmo com um grande número de arquivos:
find . -iname "*.extension" -exec sh -c '
command="<command>"
additionalParameters="<additional parameters>"
h=$(($#/2))
cmd="$command "
for i in $(seq 1 $h);do
cmd="$cmd $(eval echo \$$i) "
done
cmd="$cmd $additionalParameters"
$cmd
shift $h
$command "$@" $additionalParameters' sh {} +
Fundamentação:
Ao usar a pontuação +
, o find cria um comando o maior possível. Há duas limitações envolvidas, o número máximo de argumentos permitidos (deve ser 128k no Gnu / Linux) e o tamanho máximo da lista de argumentos (deve ser de 2 MB no Gnu / Linux). O problema é que o comando chamado requer argumentos extras (parâmetros adicionais). Adicioná-los ultrapassa o limite que leva ao "erro de muitos argumentos". Eu sugiro que o script divida a lista de parâmetros construídos em duas partes e execute dois comandos em vez de um por bloco, para que a adição de argumentos extras não exiba o problema.
Você pode usar este script:
#! /bin/bash
cmd=echo
test $# -gt 2 || exit 2
num_trailing_args="$1"
[[ $num_trailing_args =~ ^(0|[1-9][0-9]*)$ ]] ||
{ echo "Illegal first argument ('${num_trailing_args}'); aborting"; exit 2; }
test $# -lt $((num_trailing_args+2)) &&
{ echo "Too few arguments; aborting"; exit 2; }
shift
trailing_args=()
for((i=0;i<num_trailing_args;i++)); do
trailing_args[i]="$1"
shift
done
"$cmd" "$@" "${trailing_args[@]}"
e, em seguida, use
find ... -exec args_change_script.sh 3 t1 t2 t3 {} +
O nome do comando não deve ser maior que o nome do script (só para ter certeza).
Tags find