
Eu escrevi um script bash hoje durante meu horário de almoço que encontra arquivos sem extensão em um diretório e anexa uma extensão de arquivo a esses arquivos.
O script é relativamente longo porque adicionei um monte de sinalizadores e coisas como seleção de diretório e se devo copiar ou sobrescrever o arquivo, mas a essência de sua funcionalidade pode ser replicada simplesmente com isto:
#recursively find files in current directory that have no extension
for i in $(find . -type f ! -name "*.*"); do
#guess that extension using file
extfile=$(file --extension --brief $i)
#select the first extension in the event file spits something weird (e.g. jpeg/jpe/jfif)
extawk=$(echo $extfile | awk -F/ '{print $1}')
#copy the file to a file appended with the extension guessed from the former commands
cp -av $i $i.$extawk
done
É um pouco mais organizado no meu script atual - eu só queria dividir os comandos aqui para poder comentar por que estava fazendo as coisas.
Minha pergunta: Usar find
em combinação com file
da maneira que escolhi provavelmente não é a maneira mais infalível de fazer isso - qual é o problema?melhormaneira de adivinhar e acrescentar recursivamente extensões para um grupo em massa de diversos tipos de arquivos entre vários diretórios?
Responder1
for x in $(find …)
falha comnomes de arquivos contendo espaços em branco (comuns) ou caracteres curinga (um tanto incomuns). Nunca analise a saída de find
. Usar -exec
.
Zshdezmv
é conveniente para renomeação em massa.
Vamos construir um comando zmv que faça o que você deseja. Primeiro, vamos construir o padrão de pesquisa:
autoload zmv
zmv -C -o -a -n -Q '(*/)#^*.*(.)' …
-C
faz com que os arquivos sejam copiados em vez de movidos.-o -a
passa-a
paracp
.-n
significa não agir, apenas imprimir o que seria feito. Remova-o quando estiver satisfeito. Substitua por-v
se quiser agir, mas também imprimir o que está sendo feito.-Q
habilitaeliminatórias globaisno padrão.(*/)#
corresponde a zero ou mais diretórios. Ele usa o#
operador global(extended_glob
está sempre habilitado no zmv).^*.*
usa o^
operador glob para combinar arquivos sem a.
no nome.(.)
é um qualificador global que restringe as correspondências a arquivos regulares.…
será substituído pelo texto de substituição. Isso pode ser usado$f
para se referir ao nome original.
zmv
calcula todos os nomes substitutos antes de realizar qualquer substituição e reclamará se já existir algum nome substituto ou se houver conflitos. Os arquivos cujo nome substituto é idêntico ao original são ignorados.
Agora vamos construir o texto de substituição. Usaremos muitoexpansão de parâmetroscaracterísticas.
- Solicite
file
a extensão:$(file --extension --brief -- $f)
- Anexe um
.
, em preparação para a substituição:$(echo -n .; file --extension --brief -- $f)
(Isso poderia ser feito alternativamente com a expansão do parâmetro:${:-.$(…)}
.) - Se houver diversas extensões sugeridas (separadas por barras), mantenha apenas a primeira:
${$(echo -n .; file --extension --brief -- $f)%%/*}
- Se a extensão sugerida estiver vazia ou
???
, desista (substitua.
ou.???
por uma string vazia):${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}
- Anexe a extensão adicionada a
$f
(o nome original). Se o que estamos anexando estiver vazio, o arquivo permanecerá intacto.
O comando resultante:
zmv -C -o -a -n -Q '(*/)#^*.*(.)' '$f${${$(echo -n .; file --extension --brief -- $f)%%/*}:#.(|\?\?\?)}'
Isso é um pouco enigmático e você pode preferir colocar o código para gerar a substituição em uma função e usar zmv … '$(add_extension $f)'
.
Responder2
Acho que a maneira mais eficaz é comparar os tipos MIME do arquivo com o banco de dados localizado em /usr/share/mime/globs
.
- globosno Linux sãoextensão de arquivo. Exemplo dado, saída dearquivo globs
application/x-mswinurl:*.url
text/x-mrml:*.mrl
text/x-erlang:*.erl
audio/x-pn-audibleaudio:*.aa
application/x-bzip-compressed-tar:*.tbz2
application/x-netshow-channel:*.nsc
application/x-hdf:*.h4
application/pgp-keys:*.key
text/x-idl:*.idl
text/x-chdr:*.h
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.ms-powerpoint.presentation.macroEnabled.12:*.pptm
application/vnd.visio:*.vsd
application/x-hdf:*.h5
video/vnd.mpegurl:*.m4u
- depois de descrever o tipo exemplo -->
text/x-erlang
, ele diz ao Linux para identificar todos os arquivos*.
comoErlangcom extensão.erl
[glob], é por isso ->*.erl
- você pode adicionar suas próprias extensões para serem contabilizadas no
/etc/magic
arquivo
então executando o comando:
mimetype -bM file
b
argumento para apenas mostrar a vocêtype-app/extension
(breve)M
argumento significaMagiaé a maneira como o Linux verifica o arquivo em código de bytes, hexadecimal, binário para verificar se os arquivos são realmente o que afirmam ser.tipo MIMEnão retorna
/jpg/png/webp
apenas retorna um tipo e é menor quefile --mime-type file
Retorna:
image/webp
pensamentos finais
mimetype
funciona melhor comarquivos binárioscomo PDFs, imagens, vídeos. Isso porque ele pode verificar o binário, ao invés disso, text plain
é só isso, e você precisa se identificar com alguma coisa, e isso é mais complicado, é por isso que editores de texto podem reconhecer diferentes linguagens de programação, ele precisa da ajuda do usuário e de uma linguagem de servidor para cada linguagem de programação.
para recursão, eu achoárvoreestá bem:
tree -FIi '*.*' | grep -v /$
- argumento
F
é adicionar/
[barra] aos diretórios, por exemplo,folder
→folder/
- argumento
I
é selecionar o oposto do padrão*.*
[isso significa selecionar todos os arquivos com extensão], então o oposto não é extensão - argumento
i
é remover espaços da saída da árvore grep -v
é selecionar reverso, é por isso que você adiciona o/
argumento -F aárvorecomando no início, para que você possa remover diretórios e obter apenas arquivos, com/$
.
Confira mais aquitipos de mímica