Quando a string vazia indica o diretório atual?

Quando a string vazia indica o diretório atual?

Em um script que utilizo findpara coletar alguns arquivos do diretório atual, como em

$ find . -name "*.h"
./foo.h

Agora eu gostaria apenas de produzir foo.h, sem o ./prefixo. Achei que a string vazia ""denotava o diretório atual nos comandos do shell. Mas isso dá:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

Então eu estava errado. Agora, minha pergunta é quando/como/onde/.. uma "string vazia (?)" denota o diretório atual em comandos que esperam um nome de arquivo ou caminho? Existe uma explicação clara e esclarecedora?

Uma questão secundária é se as críticas acima podem ser resolvidas de forma simples, sem manipulação de strings como la ${parameter#word}ou cutor sed?

Responder1

Há muito tempo (em 7ª edição, 32V, 4.2BSD, 4.3BSD), no nível da chamada do sistema, um nome de caminho de comprimento zero denotava o diretório de trabalho atual (quando usado para pesquisa; não era permitido ao tentar criar ou excluir um arquivo ou diretório). Em Sistema III, foi um erro usar um nome de caminho de comprimento zero em todas as circunstâncias, e oPadrão POSIXtem isto a dizer sobre a resolução do nome do caminho:

Um nome de caminho nulo não será resolvido com êxito.

Responder2

Você pode usar:

find * -name "*.h"

Observe que os arquivos no diretório atual cujo nome começa com .serão omitidos, e os arquivos cujo nome começa com -serão interpretados como opções by finde causarão estragos, portanto, este não é um equivalente geral a find . ….

A ausência de uma string terminando em " /" como parte de um nome de arquivoimplicao diretório atual, mas isso não significa que o diretório atual sejadenotadopor uma string vazia (que provavelmente não é o mesmo que a ausência de uma string, embora possa parecer a mesma quando impressa).

Responder3

Em geral, a string vazia não indica o diretório atual, nem para comandos shell nem em chamadas de sistema.Aconteceu em alguns sistemas mais antigos, mas não em sistemas compatíveis com POSIX.

Ocasionalmente você encontrará um programa que usa o diretório atual quando você passa uma string vazia e o programa espera um nome de diretório. Às vezes, isso é deliberado e, às vezes, um efeito colateral de anexar o caminho absoluto do diretório atual quando a string fornecida não começa com uma barra.

O melhor para você seria deixar o ./. Não faz mal nenhum.

Se a lista de arquivos for para

find . … | sed 's!^\./!!'

Observe que isso altera alguns nomes de arquivos que contêm novas linhas. Isso geralmente não é um problema para consumo humano e o resultado de findnão é adequado para consumo de programa, pois é ambíguo. Se você estiver usando -print0, que é adequado para consumo de programa, provavelmente não se importará com o ./prefixo.

Você pode usar find * …em vez de find . …, mas observe que find *possui vários defeitos que o tornam inadequado em geral:

  • .é omitido.
  • Todos os arquivos de ponto (arquivos cujo nome começa com .ou ..`) são omitidos.
  • Se houver um nome de arquivo no diretório atual cujo nome comece com -(ou um arquivo chamado !ou (...), ele será interpretado como uma opção ou predicado por find.

O primeiro ponto não importa se o seu filtro exclui o diretório atual. Para o segundo ponto, você pode usar os padrões ..?* .[!.]* *para corresponder a todos os arquivos no diretório atual, mas será necessário verificar se cada padrão corresponde a pelo menos um arquivo e omiti-lo se não corresponder. Isto é possível, mas muito complicado. O último ponto é uma rolha. Portanto, find *pode ser adequado para uso rápido na linha de comando, mas não o use em um script.

Uma abordagem alternativa é usar o recurso globbing recursivo do shell, por exemplo

printf '%s\n' **/*.h

Isso precisa ser ativado no shopt -s globstarbash e set -o globstarno ksh93 e não existe em um shell POSIX básico, como o dash. Os arquivos de ponto não serão percorridos por padrão; para incluí-los, primeiro faça com que o globbing não ignore os arquivos de ponto no shopt -s dotglobbash ou FIGNORE='@(.|..)'no ksh93. Além disso, se não houver correspondências, este comando imprime o padrão; execute shopt -s nullglobno bash para imprimir uma linha vazia e use o padrão ~(N)**/*.hem ksh.

No zsh, o globbing recursivo está ativado por padrão. Use o qualificador glob Dpara incluir arquivos de ponto e Nimprimir uma linha vazia se não houver correspondências (por padrão, zsh gera um erro se um padrão não corresponder a nenhum arquivo). Você pode usar printfcomo acima ou

print -rl -- **/*.h(DN)

Responder4

Existem vários truques que você pode usar para obter a saída do find sem o início ./:

  1. Use a opção find -printfe diga para imprimir apenas %f. Ver man find:

    %f Nome do arquivo com todos os diretórios iniciais removidos (apenas o último elemento).

    Por exemplo:

    find . -name "*.h" -printf "%f\n"
    
  2. Analise a saída:

    find . -name "*.h" | sed 's#^./##'
    
  3. O truque de @Anthon

    find * -name "*.h"
    

Quanto à sua outra pergunta, a string vazia nunca indica o diretório atual. Acontece que vários programas tomam o diretório atual como padrão, então quando você os executa sem argumentos, eles são executados no diretório atual.

informação relacionada