Quando a string vazia denota o diretório atual?

8

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

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

Agora, gostaria que apenas saísse foo.h , sem o prefixo ./ . Eu pensei 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 / .. faz uma "cadeia vazia (?)" Denotar o diretório atual em comandos que esperam um nome de arquivo ou um nome de caminho? Existe uma explicação clara e esclarecedora?

Uma pergunta secundária é se o achado de nitidez acima pode ser resolvido de forma simples, sem manipulação de strings a ${parameter#word} ou cut ou sed ?

    
por phs 11.10.2014 / 13:07

6 respostas

11

Há muito tempo atrás (em 7ª edição , 32V , 4.2BSD , 4.3BSD ), em o nível de 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 ao usar um nome de caminho de comprimento zero em todas as circunstâncias, e o padrão POSIX tem esse para dizer sobre a resolução do nome do caminho:

A null pathname shall not be successfully resolved.

    
por 11.10.2014 / 15:14
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 por find e causam estragos, portanto, isso não é um equivalente geral para find . … .

A ausência de uma string terminando em " / " como parte de um nome de arquivo implica no diretório atual, mas isso não significa que o diretório atual seja denotado por uma string vazia (que é indiscutivelmente a mesma que a ausência de uma string, embora possa parecer a mesma quando impressa).

    
por 11.10.2014 / 13:13
4

Em geral, a string vazia não denota o diretório atual, nem os comandos do shell nem as chamadas do sistema. Funcionou 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 prefixar o caminho absoluto do diretório atual quando a string fornecida não é iniciada 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 prejudica alguns nomes de arquivos que contêm novas linhas. Isso geralmente não é um problema para consumo humano, e a saída de find não é adequada para consumo de programas, pois é ambígua. Se você estiver usando -print0 , o que é adequado para o consumo do programa, provavelmente você não se importa com o prefixo ./ .

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

  • . foi 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 começa 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 omitir se não for o caso. Isso é possível, mas muito complicado. O último ponto é uma rolha. Portanto, find * pode ser adequado para o uso de linha de comando rápida, mas não use em um script.

Uma abordagem alternativa é usar o recurso de globalização recursiva do shell, por exemplo

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

Isso precisa ser ativado por shopt -s globstar no bash e por set -o globstar no ksh93, e não existe em um shell POSIX básico como o dash. Os arquivos Dot não serão percorridos por padrão; para incluí-los, primeiro fazer globbing não ignorar arquivos de ponto com shopt -s dotglob no bash ou FIGNORE='@(.|..)' no ksh93. Além disso, se não houver correspondências, esse comando imprime o padrão; execute shopt -s nullglob no bash para imprimir uma linha vazia e use o padrão ~(N)**/*.h no ksh.

No zsh, a globalização recursiva está ativada por padrão. Use o qualificador glob D para incluir arquivos de ponto e N para imprimir uma linha vazia se não houver correspondências (por padrão, zsh lançará um erro se um padrão não corresponder a nenhum arquivo). Você pode usar printf como acima ou

print -rl -- **/*.h(DN)
    
por 12.10.2014 / 03:00
2

Não consigo pensar em nenhum exemplo em que a cadeia vazia indique o diretório atual . . Você pode estar pensando em invocações como ls , mas isso é porque ls assume o diretório atual se o parâmetro no for dado, e na verdade não usará a string vazia:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
    
por 11.10.2014 / 13:13
0

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

  1. Use a opção -printf do find e diga para imprimir apenas %f . Veja man find :

    %f                     File's name with any leading directories removed (only the last element).

    Por exemplo:

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

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

    find * -name "*.h"
    

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

    
por 11.10.2014 / 15:05
0

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

$ ls *.sh

Se você quiser também os arquivos nos subdiretórios e precisar apenas do nome do arquivo, poderá fazer 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 indica o diretório atual (e .. o diretório pai)

    
por 11.10.2014 / 15:27