Em um script que utilizo find
para 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 cut
or 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 find
e 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 find
nã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 porfind
.
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 globstar
bash e set -o globstar
no 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 dotglob
bash ou FIGNORE='@(.|..)'
no ksh93. Além disso, se não houver correspondências, este comando imprime o padrão; execute shopt -s nullglob
no bash para imprimir uma linha vazia e use o padrão ~(N)**/*.h
em ksh.
No zsh, o globbing recursivo está ativado por padrão. Use o qualificador glob D
para incluir arquivos de ponto e N
imprimir 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 printf
como 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 ./
:
Use a opção find
-printf
e diga para imprimir apenas%f
. Verman find
:%f Nome do arquivo com todos os diretórios iniciais removidos (apenas o último elemento).
Por exemplo:
find . -name "*.h" -printf "%f\n"
Analise a saída:
find . -name "*.h" | sed 's#^./##'
-
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.