Gostaria que o find
comando excluísse um determinado padrão de arquivo dentro de uma pasta, mas não as subpastas dessa pasta. Por exemplo, se eu quiser excluir subdir1/subdir1.1/UndesiredFiles*.tgz
, o seguinte não funcionará porque o asterisco corresponde a caracteres consecutivos, incluindo o separador de nome de pasta /
:
find * -not -path 'subdir1/subdir1.1/UndesiredFiles*.tgz'
O acima exclui o seguinte, que não quero excluir:
subdir1/subdir1.1/UndesiredFilesAndMore/*.tgz
subdir1/subdir1.1/UndesiredFilesAndMore/StillMore/*.tgz
Estou usando o Gnu find
versão 4.9.0.
Algumas
find
implementações suportam um-regex
predicado que é o mesmo,-path
exceto que ele usa expressões regulares (embora a variante varie com a implementação e opções ou outros predicados) em vez de padrões globais de shell. Dado que yourfind
suporta esse predicado não padrão do estilo BSD-not
, é provável que seja um deles.Onde substituímos o
*
operador glob (o mesmo que regexp.*
: 0 ou mais caracteres) por regexp[^/*]
(0 ou mais caracteres diferentes de/
).O regex é ancorado por padrão, não precisamos de um explícito
^
ou$
.Como
.
é um operador regexp que corresponde a qualquer caractere, precisamos escapá-lo\.
(embora[.]
também funcione) para que corresponda.
apenas a um literal (algo muito fácil de ignorar, pois.
s são comuns em nomes de arquivo).O
LC_ALL=C
geralmente é necessário, a menos que você possa garantir que todos os nomes de arquivos e diretórios sejam feitos apenas de caracteres válidos na localidade do usuário (isso também se aplica ao seu-path
btw).Em BSDs,
-regex
usa expressões regulares básicas padrão que podem ser alteradas para estendidas padrão com a-E
opção (como forgrep
oused
). Para GNUfind
, por padrão, são regexps de uma versão antiga do emacs, mas podem ser alterados para vários outros tipos com o-regextype
predicado . Em qualquer caso, esse regexp específico acima funcionará com qualquer variante.Com
find
s que não suportam-regex
, você pode fazer:Isso é filtrar
./subdir1/subdir1.1/UndesiredFiles*.tgz
exceto aqueles em que o que*
correspondeu incluiu pelo menos um/
.Ou você pode
perl
fazer a filtragem:Lá podemos usar
\Q...\E
o que está dentro para ser considerado como uma string fixa, eliminando a necessidade de escapar de qualquer operador regexp. Aqui, precisamos ancorar o regex no^
início e\z
no final (não$
que em perl corresponda ao final ou antes de uma nova linha no final, portanto, excluiria "erradamente" um$'UndesiredFiles.tgz\n'
arquivo).(substitua
print
porsystem "cmd", $_
para executar um comando com o caminho como argumento).Com algumas (a maioria)
find
as implementações-exec printf '%s\0' {} +
podem ser substituídas por-print0
. Algumasxargs
implementações suportam esse formato de saída com uma opção-0
ou-d '\0'
:-l
opção movida depois-0
para que o separador de registro de saída também seja um NUL.Se estiver usando o
zsh
shell, você não precisafind
, pode fazer:Onde
#
é o equivalente extendedglob de regex*
,~
é o operador except / and-not(ND)
e deve ser aplicadonullglob
(expandir para nada se não houver correspondência) edotglob
(incluir arquivos ocultos) para aquela expansão glob para corresponderfind
ao comportamento de. Você também pode adicionar ooN
qualificador paraN
ordenaro
a lista para corresponder ainda maisfind
ao comportamento de.print -rC1 --
print
s the listr
aw on1
C
olumn, mas é claro que você pode usar outro comando ou percorrer a lista em umfor
loop.**/*
(combinar arquivos com qualquer nome em qualquer quantidade de subdiretórios, abreviação de(*/)#*
) pode ser abreviado**
se você definir aglobstarshort
opção.De qualquer forma, observe que, embora todos excluam
./subdir1/subdir1.1/UndesiredFiles-whatever.tgz
, eles não excluiriam um./subdir1/subdir1.1/UndesiredFiles-whatever.tgz/other/file
. Você precisaria ajustar os padrões ou usar o-prune
find
predicado para excluí-los também.