O resultado de ls *, ls ** e ls ***

86

Eu sei que usando o comando lslistará todos os diretórios. Mas o que o ls *comando faz? Eu usei e apenas lista os diretórios. A estrela na frente dos lsmeios tem a profundidade em que pode listar os diretórios?

Andy M
fonte

Respostas:

155

lslista os arquivos e o conteúdo dos diretórios que estão sendo transmitidos como argumentos e, se nenhum argumento for fornecido, ele lista o diretório atual. Também podem ser passadas várias opções que afetam seu comportamento (consulte man lspara detalhes).

Se lsestiver sendo passado um argumento chamado *, ele procurará um arquivo ou diretório chamado *no diretório atual e o listará como qualquer outro. lsnão trata o *personagem de nenhuma outra maneira que qualquer outra.

No entanto, se ls *houver uma linha de comando do shell , o shell o expandirá de *acordo com as regras de globbing do shell correspondente (também conhecidas como Geração de nome do arquivo ou Expansão do nome do arquivo ).

Enquanto diferentes invólucros suportam diferentes operadores de globos, a maioria deles concorda com o mais simples *. *como padrão significa qualquer número de caracteres; assim *, a globserá expandido para a lista de arquivos nos diretórios atuais que correspondem a esse padrão. Há uma exceção, no entanto, que um .caractere de ponto ( ) inicial em um nome de arquivo deve ser correspondido explicitamente; portanto, ele *se expande para a lista de arquivos e diretórios que não começam .(em ordem lexicográfica).

Por exemplo, se o diretório atual contém os arquivos chamados ., .., .foo, -le foo bar, *será expandida pelo shell de dois argumentos para passar para ls: -le foo bar, por isso vai ser como se você tivesse digitado:

ls -l "foo bar"

ou

'ls' "-l" foo\ bar

Quais são as três maneiras de executar exatamente o mesmo comando. Nos 3 casos, o lscomando (que provavelmente será executado a partir /bin/lsde uma pesquisa nos diretórios mencionados em $PATH) receberá esses 3 argumentos: "ls", "-l" e "foo bar".

Aliás, neste caso, lstratará o primeiro (estritamente falando segundo ) como uma opção.

Agora, como eu disse, conchas diferentes têm operadores de globbing diferentes. Algumas décadas atrás, zshintroduziu o **/operador¹, que significa corresponder a qualquer nível de subdiretórios, abreviação (*/)#e ***/que é o mesmo, exceto pelo fato de seguir links simbólicos ao descer os diretórios.

Alguns anos atrás (julho de 2003 ksh93o+), ksh93decidiu copiar esse comportamento, mas decidiu torná-lo opcional, e apenas cobriu o **caso (não ***). Além disso, enquanto **sozinho não era especial zsh(apenas significava o mesmo que *em outros shells tradicionais, pois **significa qualquer número de caracteres seguido por qualquer número de caracteres), em ksh93, **significava o mesmo que **/*(portanto, qualquer arquivo ou diretório abaixo do atual (excluindo arquivos ocultos).

bashcopiado ksh93alguns anos mais tarde (fevereiro de 2009, bash 4.0), com a mesma sintaxe, mas com uma diferença infeliz: o bash **era como zsho ***que é, seguia links simbólicos ao retornar para subdiretórios, o que geralmente não é o que você quer fazer e pode ter efeitos colaterais desagradáveis. Foi parcialmente corrigido no bash-4.3, em que os links simbólicos ainda eram seguidos, mas a recursão parou por aí. Foi totalmente corrigido no 5.0.

yashadicionado **na versão 2.0 em 2008, ativado com a extended-globopção Sua implementação está mais próxima do zshque, por **si só, não é especial. Na versão 2.15 (2009), adicionou ***como em zshe duas de suas próprias extensões: .**e .***incluir diretórios ocultos ao repetir (em zsh, o D qualificador glob (como em **/*(D)) considerará arquivos e diretórios ocultos, mas se você quiser apenas atravessar objetos ocultos dirs, mas não expandir arquivos ocultos, você precisa ((*|.*)/)#*ou **/[^.]*(D)).

A casca do peixe também suporta **. Como na versão anterior bash, segue links simbólicos ao descer a árvore de diretórios. Nesse shell, no entanto, **/*não é o mesmo que **. **é mais uma extensão do *que pode abranger vários diretórios. Em fish, **/*.ccorresponderá, a/b/c.cmas não a.c, enquanto a**.ccorresponderá a.ce ab/c/d.ce zsh, **/.*por exemplo, deve ser escrito .* **/.*. Lá, ***é entendido como **seguido por *então o mesmo que **.

tcshtambém adicionou uma globstaropção na V6.17.01 (maio de 2010) e suporta ambos **e ***à la zsh.

Assim, em tcsh, bashe ksh93, (quando a opção correspondente está habilitado ( globstar)) ou fish, **se expande todos os arquivos e diretórios abaixo do atual, e ***é o mesmo que **para fish, um link simbólico atravessando **para tcshcom globstar, e o mesmo que *no bashe ksh93(apesar de ser não é impossível que versões futuras desses shells também atravessem links simbólicos).

Acima, você notou a necessidade de garantir que nenhuma das expansões seja interpretada como uma opção. Para isso, você faria:

ls -- *

Ou:

ls ./*

Existem alguns comandos (não importa ls) onde o segundo é preferível, pois mesmo com --alguns nomes de arquivos podem ser tratados especialmente. É o caso de -para a maioria dos utilitários de texto, cde pushde nomes de arquivos que contenham o =caractere para awkpor exemplo. Anexar ./a todos os argumentos remove seu significado especial (pelo menos nos casos mencionados acima).

Também deve-se observar que a maioria dos shells possui várias opções que afetam o comportamento de globbing (como arquivos de ponto são ignorados ou não, a ordem de classificação, o que fazer se não houver correspondência ...), consulte também o $FIGNOREparâmetro emksh

Além disso, em todos os shell mas csh, tcsh, fishe zsh, se o padrão de englobamento não corresponde a qualquer arquivo, o padrão é passado como um argumento não expandidas, que causa confusão e possivelmente bugs. Por exemplo, se não houver arquivo não oculto no diretório atual

ls *

Na verdade, chamará lscom os dois argumentos lse *. E como não há nenhum arquivo, também não foi chamado *, você verá uma mensagem de erro de ls (não do shell) como:, ls: cannot access *: No such file or directoryque é conhecida por fazer as pessoas pensarem que era isso lsque estava realmente expandindo os globs.

O problema é ainda pior em casos como:

rm -- *.[ab]

Se não há *.anem *.barquivo no diretório atual, então você pode acabar a exclusão de um arquivo chamado *.[ab]por engano ( csh, tcshe zshiria relatar um páreo erro e não chamaria rm(e fishnão suporta os [...]wildcards)).

Se você deseja passar um literal *para ls, é necessário citar esse *caractere de alguma forma, como em ls \*ou ls '*'ou ls "*". Em shells do tipo POSIX, o globbing pode ser totalmente desabilitado usando set -o noglobou set -f(o último não funciona a zshmenos que seja em sh/ kshemulado).


¹ Embora (*/)#sempre tenha sido suportado, ele foi o primeiro em short-handed, como ..../no zsh-2.0 (e potencialmente antes), depois ****/no 2.1 antes de obter sua forma definitiva **/no 2.2 (início de 1992)

Stéphane Chazelas
fonte
22
Realmente ótima resposta!
Andrea Corbellini
7
Outro bom exemplo é find -name *. Especialmente com padrões mais complexos, se houver exatamente uma correspondência no diretório atual, as pessoas geralmente não perceberão que não estão passando o asterisco find.
njsg
Continuo a dar +1, estou muito feliz em ver Stephane Chazelas ativo aqui no unix.stackexchange.com ! Grandes contribuições, Stephane, como sempre!
Dimitre Radoulov
11
No peixe, *.[ab]tentaria excluir tudo que termina com .[ab]. [não é especial em peixes.
amigos estão dizendo sobre Konrad Borowski
35

O comando lspadrão é ls .: Listar todas as entradas no diretório atual .

O comando ls *significa 'executar sl na expansão do *padrão de shell'

O *padrão é processado pelo shell e se expande para todas as entradas no diretório atual, exceto aquelas que começam com a .. Irá um nível profundo.

A interpretação de *padrões duplos ou triplos depende da casca real usada.

*é um curinga que corresponde a 0 ou mais caracteres. Algumas conchas modernas serão recursivas em subdiretórios ao ver o **padrão.

Dennis Kaarsemaker
fonte
17
Exceto esse extra * fazer acrescentar algo, veja a outra resposta explicando a globstar. Também deve ficar claro que ls não tem nada a ver com os asteriscos, nunca passou por nenhum desses asteriscos. Corra echo ls *para ver o que seria executado quando você escrever ls *.
njsg
12

Você pode desmistificar todo o processo digitando em echovez de lsprimeiro, para ver o que o comando se expande:

$ echo *
Applications Downloads Documents tmp.html

Portanto, neste caso, ls *expande parals Applications Downloads Documents tmp.html

$ echo **
Applications Downloads Documents tmp.html

$ echo ***
Applications Downloads Documents tmp.html

Portanto, nenhuma mudança. Isso pressupõe que você esteja usando bashcomo seu shell - a maioria das pessoas e conchas diferentes têm um comportamento diferente. Se você estiver usando ashou cshou kshou zsh, poderá esperar que as coisas funcionem de maneira diferente. Esse é o ponto de ter conchas diferentes.

Então, vamos tentar algo diferente (ainda com bash) para que possamos ter uma idéia do que o *operador globbing ( ) pode fazer por nós. Por exemplo, podemos filtrar por parte do nome:

$ echo D*
Downloads Documents

E, curiosamente, uma barra final é uma parte implícita de qualquer nome de diretório. Portanto */, produzirá apenas os diretórios (e links simbólicos para os diretórios):

$ echo */
Applications/ Downloads/ Documents/

E podemos filtrar em vários níveis colocando barras no meio:

$ echo D*/*/
Documents/Work/ /Documents/unfinished/

Como o Downloadsdiretório não contém subdiretórios, ele não termina na saída. Isso é muito útil apenas para examinar os arquivos que você deseja. Eu uso comandos como este o tempo todo:

$ ls -l /home/*/public_html/wp-config.php

Isso lista, se houver, todos os wp-config.phparquivos que existem no nível base do public_htmldiretório de qualquer usuário . Ou talvez para ser mais completo:

$ find /home/*/public_html/ -name wp-config.php

Ele encontrará todos os wp-config.phparquivos nos public_htmldiretórios de qualquer usuário ou em seus subdiretórios, mas operará com mais eficiência do que apenas find /home/ -name wp-config.phpporque não examinará nada além dos public_htmldiretórios de cada usuário.

tylerl
fonte
11
"Isso pressupõe que você esteja usando bashcomo seu shell" ← e que a globstar não está ativada. shopt -s globstare tente novamente ...
njsg 27/01
Outra maneira de desmistificar é a set -xque começará a imprimir o comando "real" executado a cada vez (desligue com set +x).
ShreevatsaR
10

Em algumas shells, incluindo o bash 4.x com a globstaropção ativada, **será executado um globo recursivo, diretórios correspondentes descendentes. Asteriscos adicionais não modificam mais esta operação.

Ignacio Vazquez-Abrams
fonte
11
Porém, como eu disse na minha resposta, tenha cuidado com o contrário de , ksh93e atravessa links simbólicos na recursão que geralmente é indesejável. zshbash
Stéphane Chazelas
Isso foi corrigido no bash 4.3.
Stéphane Chazelas
1

Se você quiser "mergulhar fundo", use a opção ls -R (recursive) ou use find, da seguinte maneira:

find . -ls

"find" irá mergulhar na parte inferior da árvore de diretórios (como será 'ls -R') e possui muitas outras opções, como listar diretórios (-type d), somente arquivos (-type f) ou mostrar arquivos com outras características (nenhum usuário em / etc / passwd, permissões específicas e muito mais). O "find" também é um pouco mais seguro nos scripts (devido a regras inconsistentes de globbing entre shells, bem como escapes especiais para arquivos com traços, etc).

globbing de curinga shell não funcionará apenas com um asterisco '*' nos arquivos de ponto. Para listar apenas arquivos de ponto, use:

ls .??*

Razzlephrazz
fonte
-1

Extra * não adiciona nível de profundidade. Mas se você tentar

 ls */*/*

- você receberá uma lista de subpastas de subpastas em pastas na pasta atual ...

VB9-UANIC
fonte
Errado. Dependendo do shell, *s extras adicionam nível de profundidade.
Njsg
então, qual shell adicionará nível de profundidade a estrelas extras?
VB9-UANIC
De várias. Incluindo o GNU bashcom a opção globstar, o korn shell e o zsh. E possivelmente outros, eu acho. unix.stackexchange.com/a/62665/14831
njsg
-1

Minha principal preocupação é que echo ** / *. Ext geralmente ...

Pode ou não: lista um arquivo .ext no diretório atual

Pode ou não: incluir arquivos. *. Ext

Geralmente, eu preferiria que a expressão glob fosse '** /' e que pode resultar em uma string nula (sem subdiretório). É assim que eu converto expressões glob na entrada do programa. É claro que garanto que existem comentários explicando isso em exemplos de uso.

anthony
fonte