Sobrescrever arquivos encontrados por find?

4

Eu tenho uma lista de arquivos

-rw-rw-r-- 1 t t 24813 Oct 23  2002 fig8_21.gif
-rw-rw-r-- 1 t t  2259 Oct 23  2002 fig8_21t.gif
-rw-rw-r-- 1 t t 35331 Oct 23  2002 fig8_23.gif
-rw-rw-r-- 1 t t  2610 Oct 23  2002 fig8_23t.gif
-rw-rw-r-- 1 t t 19970 Oct 23  2002 fig8_24.gif
-rw-rw-r-- 1 t t  2019 Oct 23  2002 fig8_24t.gif
-rw-rw-r-- 1 t t 54623 Oct 23  2002 fig8_3.gif
-rw-rw-r-- 1 t t  3657 Oct 23  2002 fig8_3t.gif
-rw-rw-r-- 1 t t 35861 Oct 23  2002 fig8_4.gif
-rw-rw-r-- 1 t t  2344 Oct 23  2002 fig8_4t.gif

Desejo substituir <...>t.gif por <...>.gif , por exemplo copie fig8_4.gif para sobrescrever fig8_4t.gif .

Primeiro, localizo esses <...>t.gif arquivos por find . -regex ".*t\.gif" . Então eu quero usar basename para remover os nomes dos arquivos, mas por que eu tenho os seguintes avisos?

$ find .  -regex ".*t\.gif" -execdir basename {} \;  
find: The relative path '~/program_files/internet/SSH_tunneling/' is included in the PATH environment variable, which is insecure in combination with the -execdir action of find.  Please remove that entry from $PATH

$ find .  -regex ".*t\.gif" | xargs basename 
basename: extra operand ‘./f10_1t.gif’

Como devo continuar a terminar minha tarefa?

É possível não usar find ?

    
por Tim 08.05.2015 / 06:58

4 respostas

4

Sim, é possível. Uma maneira de fazer isso pode ser assim:

cd /den/of/gifs && \
for f in ./*t.gif; do
    mv -- "${f%%t.gif}.gif" "$f"
done

A coisa ${var%%pattern} é a sintaxe padrão / POSIX sh para remover a string mais longa que corresponde ao padrão do final de $var .

    
por 08.05.2015 / 07:08
2

Para torná-lo ainda mais compacto e rápido, você pode usar parallel :

parallel mv {} {.}t.gif  :::: <(find . -regex '.+[0-9].gif')

A expressão após :::: fornece argumentos para parallel . Esses argumentos são usados usando {} . O {.} representa o argumento sem a extensão do arquivo. Então, no nosso caso, {} serão os nomes dos arquivos sem o t , por exemplo, %código%. fig8_4.gif seria então {.} .

    
por 08.05.2015 / 07:59
2

Se você quiser ficar com find e basename , isso deve funcionar:

find . -name "*.gif" ! -name "*t.gif" -execdir sh -c '
  cp -- "$0" "$(basename "$0" .gif)t.gif"' {} \;

Isso não é a melhor opção em desempenho e recursos.

    
por 08.05.2015 / 08:11
2

Sobre seus erros:

find: The relative path '~/program_files/internet/SSH_tunneling/' is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH

Você tem ~/program_files/internet/SSH_tunneling/ no seu $PATH . Isso é um literal ~ . Isso não significa seu diretório home . Em vez disso, isso significa o caminho relativo ~/program_files/internet/SSH_tunneling/ , que é SSH_tunneling dentro de internet dentro de program_files dentro do diretório ~ dentro do diretório atual.

-execdir não gosta de diretórios relativos em $PATH porque faz chdir() nos diretórios em que executa os comandos para executar. Por exemplo, se ao descer a árvore de diretórios, find encontra um arquivo gif em ./some-dir que também contém um diretório ~ , e há um ./some-dir/~/program_files/internet/SSH_tunneling/basename , então ele pode acabar executando aquele basename .

Você provavelmente tem um:

PATH="~/program_files/internet/SSH_tunneling/"

no seu ~/.bash* , que deveria ter sido:

PATH=~/program_files/internet/SSH_tunneling/

ou

PATH="$HOME/program_files/internet/SSH_tunneling"
 $ find .  -regex ".*t\.gif" | xargs basename 
 basename: extra operand ‘./f10_1t.gif’

xargs executará basename com o maior número de argumentos possível. basename espera no máximo 2 argumentos, o primeiro um caminho, o segundo um padrão. Então seu comando não faz sentido.

Também xargs , por padrão, espera uma lista separada em branco (espaço, tabulação e possivelmente mais dependendo da localidade e da implementação find ) e nova linha (todos podem ocorrer em nomes de arquivos) na entrada com tratamento especial de aspas, que não é o formato de saída de find .

find produzirá algo como:

./path/to/picture from "The End of the World".gif

Qual xargs entenderá como três argumentos diferentes: ./path/to/picture , from , The End of the World.gif . find não tem modo de saída no formato esperado por xargs , aqui:

./path/to/picture\ from\ \"The\ End\ of\ the\ World\".gif

ou

'./path/to/picture from "The End of the World".gif'

Algumas implementações de xargs suportam uma opção -0 para manipular uma lista delimitada por NUL em vez de em branco / nova linha + aspas um, e algumas implementações find têm -print0 predicado para gerar nomes de arquivo com esse formato ( naqueles que não o fizerem, você pode usar -exec printf '%szsh' {} + ).

Então, algo como:

find .  -name "*t.gif" -print0 | xargs -r0 -I {} basename {} t.gif

que seria de pouca utilidade para o seu objetivo geral. Em vez disso, você provavelmente quer algo como:

find . -name '*t.gif' -type f -exec sh -c '
  for f do
    cp "${f%t.gif}.gif" "$f"
  done' sh {} +

Ou com %code% :

autoload zmv # if not already in ~/.zshrc
zmv -C '(**/)(*)t.gif(#qD.)' '$1$2.gif'
    
por 08.05.2015 / 11:48