Encontre o comando if filename não existe no diretório

3

Como eu quero exibir a pasta que não possui determinado arquivo. Mas a preocupação é que o arquivo é o mesmo nome, mas casos diferentes.

Estudo de caso: No diretório de ferramentas, há subdiretórios que contêm readme / README e alguns deles não têm. Por exemplo

/toola/readme
/toolb/README
/toolc/ (does not have readme file)

Eu quero encontrar o comando para exibir apenas a pasta toolc usando este comando.

find . -maxdepth 2 ! -name '*readme*' -o ! -name '*README*' | awk -F "/" '{print $$2}' | uniq

Mas isso não funciona. Ele exibe todos os arquivos, pois toola não tem README e toolb não tem readme

    
por daffodil 24.10.2018 / 07:05

5 respostas

9

Você não pode usar find para procurar por arquivos que não existem. No entanto, você pode usar find para procurar diretórios e, em seguida, testar se os nomes de arquivos fornecidos existem nesses diretórios.

Ao usar find para procurar diretórios, certifique-se de usar -type d . Em seguida, teste cada um dos diretórios encontrados para os arquivos README e readme .

Assumindo a hierarquia de diretório a seguir para algum diretório superior projects :

projects/
|-- toola
|   |-- doc
|   |-- readme
|   '-- src
|-- toolb
|   |-- doc
|   '-- src
|-- toolc
|   |-- README
|   |-- doc
|   '-- src
'-- toold
    |-- doc
    '-- src

Usando find para encontrar os diretórios diretamente em projects que não contém um arquivo README ou readme :

$ find projects -mindepth 1 -maxdepth 1 -type d \
    ! -exec test -f {}/README ';' \
    ! -exec test -f {}/readme ';' -print
projects/toolb
projects/toold

Aqui, encontramos qualquer diretório diretamente sob projects e, em seguida, usamos o utilitário test para determinar qual dos diretórios encontrados não contém nenhum dos dois arquivos.

Isso é exatamente o equivalente de

find projects -mindepth 1 -maxdepth 1 -type d \
    -exec [ ! -f {}/README ] ';' \
    -exec [ ! -f {}/readme ] ';' -print

Outra formulação dos itens acima:

find projects -mindepth 1 -maxdepth 1 -type d -exec sh -c '
    for pathname do
        if [ ! -f "$pathname/README" ] &&
           [ ! -f "$pathname/readme" ]; then
            printf "%s\n" "$pathname"
        fi
    done' sh {} +

Aqui, deixamos um pequeno script de shell em linha fazer o teste real dos dois arquivos e imprimir o nome do caminho dos diretórios que não contêm nenhum deles. O utilitário find age como um "gerador de nome de caminho" de nomes de caminho para diretórios para o script in-line para iterar.

Na verdade, se a estrutura do diretório for assim, podemos optar por não usar find :

for pathname in projects/*/; do
    if [ ! -f "$pathname/README" ] &&
       [ ! -f "$pathname/readme" ]; then
        printf '%s\n' "$pathname"
    fi
done

Observe a barra no final no padrão projects/*/ . É isso que faz com que o padrão corresponda apenas aos diretórios (ou links simbólicos para diretórios).

A diferença entre fazer isso e usar find é que, com o loop de shell acima, excluiremos diretórios ocultos em project e incluiremos links simbólicos para diretórios.

Em todos os casos, iteramos sobre os nomes de caminho dos diretórios e testamos a inexistência dos dois nomes de arquivos.

A única ressalva é que o teste -f também será verdadeiro para um link simbólico para um arquivo normal.

Relacionados:

por 24.10.2018 / 07:44
3

com zsh :

set -o extendedglob # for (#i) for case insensitive matching

all_projects=(projects/*(-/))
typeset -aU projects_with_readme # -U for unique
projects_with_readme=(projects/*/(#i)readme(:h))
projects_without_readme=(${all_projects:|projects_with_readme})

echo Projects with READMEs:
printf ' - %s\n' $projects_with_readme
echo Projects without READMEs:
printf ' - %s\n' $projects_without_readme

Você pode alterar o (#i)readme para (#i)*readme* para os arquivos chamados README.txt ou 000README , ou o (:h) para (-.:h) para considerar apenas os arquivos readme que são regular após a resolução do link simbólico (excluir diretórios, links quebrados e outros tipos especiais de arquivos).

    
por 24.10.2018 / 08:30
3

Considerando que eu voto na clara e elegante solução da Kusalananda, acrescento que esse tipo de tarefa parece como operar em conjuntos. Uma ferramenta find pura sozinha não se encaixa bem. De fato, ele precisa trazer ferramentas externas usando -exec .

Uma abordagem diferente poderia estar usando uma ferramenta de comparação / comparação. Por exemplo, supondo que você tenha acesso ao GNU find :

comm -2 -3 <(find ./tool* -maxdepth 0 -type d | sort) \
<(find ./tool* -iname "readme" -printf "%H\n" | sort)

Onde:

  • comm compara dois arquivos classificados linha por linha; as opções -2 -3 permitem remover de seus resultados de saída que estão apenas no segundo arquivo ou em ambos os arquivos.
  • -printf "%H\n" permite que find imprima apenas o ponto inicial no qual o arquivo foi encontrado, seguido por uma nova linha (temos que corresponder à opção -maxdepth 0 que define a outra lista).

Testado com a árvore:

$ find ./tool* -printf "%p %y\n" | sort
./toola d
./toola/doc d
./toola/readme f
./toola/src d
./toolb d
./toolb/doc d
./toolb/src d
./toolc d
./toolc/doc d
./toolc/README f
./toolc/src d
./toold d
./toold/doc d
./toold/doc/readme f
./toold/src d

O comando acima fornece:

./toolb
    
por 24.10.2018 / 10:33
0

Esta é uma resposta parcial, mas havia muito para escrever como comentário. Existem várias coisas erradas com este comando.

Em primeiro lugar, sua lógica está errada. Você provavelmente deseja -a em vez de -o . Seu comando:

find . -maxdepth 2 ! -name '*readme*' -o ! -name '*README*'

encontrará arquivos que (não têm readme neles) OU (não tem README neles). Se você simplesmente executar o seu comando, verá que ele retorna todos os arquivos da sua árvore. Assim, você poderia usar

find . -maxdepth 2 ! -name '*readme*' -a ! -name '*README*' 

Em segundo lugar, você provavelmente nem precisa dessa construção. Se você tiver uma leitura de man find , poderá ver que existe uma opção chamada -iname , que é uma versão que não diferencia maiúsculas de minúsculas de -name . Portanto, você pode fazer

find . -maxdepth 2 ! -iname '*readme*'

Finalmente, você pode ver que, se você executar esses comandos, ele retornará qualquer coisa que não tenha a string em seu nome. Assim, os diretórios pai, incluindo toola e toolb aparecerão, porque eles não têm a string em seu nome. Isso é esperado, porque, se você observar a saída, não haverá readme ou README na linha. Isso é apenas o arquivo dentro do diretório.

    
por 24.10.2018 / 07:31
0

Eu usaria o seguinte:

find -type d -maxdepth 2 -not -name '*readme*' | awk -F "/" '{print $$2}' | uniq
    
por 24.11.2018 / 15:53

Tags