Quando a cadeia vazia indica o diretório atual?

8

Em um script que eu uso findpara coletar alguns arquivos no diretório atual, como em

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

Agora eu gostaria que fosse apenas saída foo.h, sem o ./prefixo. Eu pensei que a seqüência vazia ""denotava o diretório atual em comandos 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 (?)" Indica o diretório atual em comandos que esperam um nome de arquivo ou nome de caminho? Existe uma explicação clara e esclarecedora?

Uma questão paralela é se a busca detalhada acima pode ser resolvida de maneira simples, sem a manipulação de strings a la ${parameter#word}ou cutor sed?

phs
fonte
1
findpossui o parâmetro -printf para manipular como os resultados são mostrados. find . -name '*.h' -printf '%P\n'irá remover o ./prefixo. Veja o que find . -name '*.h' -printfaz.
Valentin Bajrami
Obrigado! Pena que minha versão do find não suporta a -printfopção. FWIW, stringsna minha descoberta dá @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
Phs #
5
@phs é o tipo de coisa que torna essencial que você sempre mencione seu sistema operacional.
terdon

Respostas:

11

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

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

Mark Plotnick
fonte
3
Uma sequência vazia em $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (e outras, mas não necessariamente todas as $ * PATH) indica o diretório atual. dir/é principalmente o mesmo quedir/.
Stéphane Chazelas
4

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 finde causam estragos, portanto, isso não é um equivalente geral a find . ….

A ausência de uma sequência terminada em " /" como parte de um nome de arquivo implica o diretório atual, mas isso não significa que o diretório atual seja indicado por uma sequência vazia (que não pode ser a mesma que a ausência de uma sequência, embora possa parecer o mesmo quando impresso).

Anthon
fonte
4

Em geral, a cadeia vazia não indica o diretório atual, nem para shell comandos nem nas chamadas do sistema. Isso ocorreu 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 passa uma string vazia e o programa espera um nome de diretório. Isso às vezes é deliberado e, às vezes, um efeito colateral de preceder o caminho absoluto do diretório atual quando a sequência especificada não inicia com uma barra.

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

Se a lista de arquivos for para

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

Observe que isso controla alguns nomes de arquivos que contêm novas linhas. Isso geralmente não é um problema para o consumo humano, e a saída de findnão é adequada para o consumo do programa, pois é ambígua. Se você estiver usando -print0, o que é adequado para o consumo do programa, provavelmente não se importa com o ./prefixo.

Você pode usar em find * …vez de find . …, mas observe que find *há 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 precisará verificar se cada padrão corresponde a pelo menos um arquivo e omitir se não corresponder. Isso é possível, mas muito complicado. O último ponto é uma rolha. Portanto, find *pode ser adequado para o uso rápido da linha de comando, mas não o use em um script.

Uma abordagem alternativa é usar o recurso de globbing recursivo da concha, por exemplo

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

Isso precisa ser ativado shopt -s globstarno bash e set -o globstarno ksh93 e não existe em um shell POSIX básico, como o dash. Arquivos de ponto não serão percorridos por padrão; Para incluí-los, faça com que o globbing não ignore os arquivos de ponto shopt -s dotglobno bash ou FIGNORE='@(.|..)'no ksh93. Além disso, se não houver correspondências, esse comando imprime o padrão; execute shopt -s nullglobno bash para imprimir uma linha vazia e use o padrão ~(N)**/*.hno 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, 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)
Gilles 'SO- parar de ser mau'
fonte
Obrigado por todas essas dicas. A maioria dos defeitos find *são novos para mim e alguns são assustadores!
Phs12
2

Não consigo pensar em nenhum exemplo em que a cadeia vazia denote o diretório atual .. Você pode estar pensando em invocações como ls, mas isso ocorre porque lsassume o diretório atual se nenhum parâmetro for fornecido e, na verdade, ele não aceitará a string vazia:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ulrich Schwarz
fonte
3
Um exemplo em que a cadeia vazia indica o diretório atual é como um PATHcomponente.
hvd 11/10
0

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

  1. Use a -printfopção encontrar e diga para imprimir apenas %f. Veja man find:

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

    Por exemplo:

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

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

    find * -name "*.h"
    

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

terdon
fonte
Isso assume o GNU find. Ou use %Papenas para retirar o ./
código
1
find * -name '*.h'irá encontrar foo/.bar.h, mas não .bar.hno diretório atual.
Stéphane Chazelas
0

Se os arquivos estiverem no diretório atual, você pode apenas usar ls:

$ ls *.sh

Se você quiser os arquivos nos subdiretórios também e apenas precisar do nome do arquivo, faça algo como:

$ find . -name '*.h' -exec basename {} \;

Existem comandos que obterão a ausência de uma string como o diretório atual, mas basicamente porque eles obterão o diretório atual como padrão se nada for inserido. O .sempre denotar a dir atual (e ..o diretório pai)

Karl
fonte
o OP queria apenas ./ removido, enquanto a sua findsolução remove todos os componentes do diretório
artm