é o conteúdo de um diretório ignorando links simbólicos

16

Eu tenho um diretório no qual gostaria de listar todo o conteúdo (arquivos e subdiretórios) sem mostrar os links simbólicos. Estou usando utilitários GNU no Linux. A lsversão é 8.13.

Exemplo:

Lista completa de diretórios:

~/test$ ls -Gg
total 12
drwxrwxr-x 2 4096 Jul  9 10:29 dir1
drwxrwxr-x 2 4096 Jul  9 10:29 dir2
drwxrwxr-x 2 4096 Jul  9 10:29 dir3
-rw-rw-r-- 1    0 Jul  9 10:29 file1
-rw-rw-r-- 1    0 Jul  9 10:29 file2
lrwxrwxrwx 1    5 Jul  9 10:29 link1 -> link1
lrwxrwxrwx 1    5 Jul  9 10:30 link2 -> link2

O que eu gostaria de receber

~/test$ ls -somthing (or bash hack)
total 12
dir1 dir2 dir3 file1 file2

NOTA: Minha principal motivação é fazer um grep recursivo (GNU grep 2.10) sem seguir os links simbólicos.

TheMeaningfulEngineer
fonte

Respostas:

28

Para a pergunta declarada, você pode usar find:

find . -mindepth 1 ! -type l

listará todos os arquivos e diretórios no diretório atual ou todos os subdiretórios que não são links simbólicos.

mindepth 1é apenas para pular a .entrada do diretório atual. A carne é a combinação de -type l, o que significa "é um elo simbólico" e !, o que significa negar o teste a seguir. Em combinação, eles correspondem a todos os arquivos que não são um link simbólico. Isso lista todos os arquivos e diretórios recursivamente, mas sem links simbólicos.

Se você deseja apenas arquivos regulares (e não diretórios):

find . -type f

Para incluir apenas os filhos diretos deste diretório, e não todos os outros recursivamente:

find . -mindepth 1 -maxdepth 1

Você pode combinar esses (e outros) testes para obter a lista de arquivos que deseja.

Para executar um determinado grepem cada arquivo que corresponde aos testes que você está usando, use -exec:

find . -type f -exec grep -H 'some pattern' '{}' +

O '{}'será substituído pelos arquivos. O +é necessário dizer findo seu comando é feito. A opção -Hforça o grep a exibir um nome de arquivo, mesmo que seja executado com um único arquivo correspondente.

Michael Homer
fonte
Ele não mostra os arquivos no qual o padrão foi encontrado.
TheMeaningfulEngineer
É para isso que você corre grep(veja o último parágrafo).
Michael Homer
Eu não trabalho para mim. Ele apenas gera os padrões encontrados sem um nome de arquivo onde foi encontrado.
TheMeaningfulEngineer
2
Use grep -H, se você o tiver (consulte man grep), ou faça grep 'pat' '{}' /dev/null ';'-o pensar que existem vários arquivos, se não o tiver.
Michael Homer
11
+falha em atender a esse requisito no caso degenerado em que existe apenas um arquivo ou um arquivo no final.
Michael Homer
16

Ou, mais simples:

ls -l | grep -v ^l

Explicação

ls -lsignifica listar em formato longo . Quando você faz isso, a primeira sequência de caracteres fornece informações sobre cada arquivo. O primeiro caractere indica que tipo de arquivo é. Se é um link simbólico, então lé o primeiro caractere.

grep -v ^lsignifica filtrar ( -v) as linhas que começam com ( ^) an l.

Jonathan Petitcolas
fonte
Isso funciona muito bem, mas como obter apenas nomes de pastas? Eu quero usar isso emfor i in
Luka
Você pode explicar esse código, por favor?
ABG
Tenha em atenção que esta solução não funciona se omitir -lem ls. Então, isso não iria funcionar como esperado:ls | grep -v ^l
stamster
@Luka Você não repassa a saída de find, consulte unix.stackexchange.com/questions/321697
Kusalananda
11
@abg - eu adicionei uma explicação. Espero que ajude.
Geoff
7

A partir da versão 2.12, a -ropção GNU grep não desrefere links simbólicos, a menos que você os especifique manualmente:

-r, --recursive

Leia todos os arquivos em cada diretório, recursivamente, seguindo os links simbólicos apenas se estiverem na linha de comando. Isso é equivalente à opção -d recurse.

-R, --dereferência-recursiva

Leia todos os arquivos em cada diretório, recursivamente. Siga todos os links simbólicos, ao contrário de -r.

tijagi
fonte
Qual versão do grep é essa? Eu tenho -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.em 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
tijagi
5

No zsh, isso seria fácil graças aos qualificadores da glob :

grep -- PATTERN **/*(.)

O padrão **/percorre subdiretórios recursivamente. O qualificador global .restringe a correspondência aos arquivos regulares.

Sem o zsh, use find(veja a resposta de Michael Horner ). E nesse caso em particular, o GNU grep pode fazer o que você deseja (é exatamente o que grep -rfaz) - mas somente desde a versão 2.12, as versões anteriores seguiram links simbólicos.

Gilles 'SO- parar de ser mau'
fonte
2

Você pode usar $LS_COLORSpara fazer isso. Se sua versão do lssuporta a especificação das cores usando essa variável, é possível definir a saída por tipo de arquivo. É um comportamento embutido e muito configurável. Então, eu criei alguns arquivos para demonstrar isso como:

for f in 9 8 7 6 5 4 3 2 1
    do touch "${f}file" && 
    ln -s ./"${f}file" ./"${f}filelink"
done

Então agora eu vou fazer:

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | cat

###OUTPUT###
1file


HERE_THERE_BE_A_LINK>>1filelink@
2file


HERE_THERE_BE_A_LINK>>2filelink@
3file

...
HERE_THERE_BE_A_LINK>>8filelink@
9file
...

E os nulos também estão lá ...

LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \
ls -1 --color=always | sed -n l
1file$
$
$
\000HERE_THERE_BE_A_LINK>>\0001filelink@$
2file$
$
$
\000HERE_THERE_BE_A_LINK>>\0002filelink@$
3file$
...

Você pode especificar para todos ou quaisquer tipos de arquivos. Fazer isso apenas para um único tipo de arquivo pode não o desejar, pois lsincorporou alguns valores de compilação padrão para escapes de terminal. Você faria muito melhor abordar a API como uma única interface. Aqui está um pequeno meio simples de analisar e atribuir dircolorspadrões configurados no ambiente atual :

LS_COLORS='rs=:no=//:lc=:rc=:ec=//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//:"
done) ls -l --color=always | cat

Sua saída no meu diretório pessoal é assim:

total 884
///-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//1/
//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//Desktop//
//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//Terminology.log/
//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//new
file/
//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//new
file
link/ -> /fi//./new
file/
//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//script.sh/*
//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//shot-2014-06-22_17-10-16.jpg/
//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//target.txt/

Você também pode executá-lo cat -Ae a única diferença que encontrará é que verá $novas linhas - não há caracteres imprimíveis introduzidos por ls --color=alwaysessa configuração - apenas o que você vê aqui.

ls insere seu terminal padrão como este:

${lc}${type_code}${rc}FILENAME${lc}${rs}${rc}

... onde os valores padrão para $lc (à esquerda do código) , $rc (à direita do código) e $rs (redefinir) são:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...respectivamente. ${type_code}é usado para substituir os vários fi (arquivo regular - padrão não definido) , di (diretório) , ln (link) e todos os outros tipos de arquivos que eu conheço. Também há $no (normal) que também é, por padrão, desativado e que é representado aqui pelo //no início de cada linha. Meu pequeno IFS=:bloco simples funciona apenas inserindo o nome de cada configurável também como seu próprio valor e adicionando uma barra ou duas - embora \0NUL bytes funcionassem também.

Por padrão ls, também será inserido um $rsimediatamente anterior à sua primeira saída $lc- mas isso não é representado com precisão aqui. Nesse caso, eu especifiquei $ec (código final) que representa $rstodos os casos - quando especificado, você não recebe um extra $rsentre $noe ${type_code}como faria de outra maneira - ele só é apresentado imediatamente após um nome de arquivo e uma vez no início da saída - como você pode ver na barra extra no início da linha um.

Aqui está um trecho do meu próprio $LS_COLORS

printf %s "$LS_COLORS"

rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:\
so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:\
or=40;31;01:su=37;41:sg=30;43:ca=30;41:\
tw=30;42:ow=34;42:st=37;44:ex=01;32:\
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:...

E, na verdade, meu pequeno shell hack é provavelmente muito complicado - há uma interface amplamente disponível para atribuir esses valores. Tente dircolors -pem sua cli e info dircolorspara mais informações sobre isso.

Você pode agrupar os nomes de arquivos em cadeias arbitrárias. Você pode comentá-los, se desejar. Você pode especificar comportamentos semelhantes com base apenas na extensão do arquivo. Não há realmente muita coisa que você não possa especificar dessa maneira.

Agora também não estou inventando tudo isso - aprendi sobre isso depois de tropeçar no código fonte por acidente.

Com esta configuração particular lsemitirá:

  1. $no - uma vez por registro no início de cada registro

  2. ${type_code}- uma vez imediatamente antes de cada nome de arquivo para incluir uma abreviação do tipo de arquivo e sempre ocorrendo na mesma linha que os campos delimitados por 7 espaços em branco, após $no ou imediatamente após a ->indicação do destino de um link simbólico.

  3. $ec - uma vez imediatamente antes da primeira linha e depois apenas uma vez imediatamente após cada nome de arquivo.

  4. Todos os outros valores estão vazios.

O que se segue é um nulo-delimitado ls, e desta vez usarei cat -A, porém, sem ele, seria o mesmo que no último exemplo:

LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$(
set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=/$fc//\0:"
done) ls -l --color=always | cat -A

total 884$
^@//^@//-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 /fi//^@1^@//$
^@//drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 /di//^@Desktop^@///$
^@//-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 /fi//^@Terminology.log^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 /fi//^@new$
file^@//$
^@//lrwxrwxrwx 1 mikeserv mikeserv     10 Jul 11 04:18 /ln//^@new$
file$
link^@// -> /fi//^@./new$
file^@//$
^@//-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 /ex//^@script.sh^@//*$
^@//-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 /fi//^@shot-2014-06-22_17-10-16.jpg^@//$
^@//-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 /fi//^@target.txt^@//$

E, para remover de forma confiável todos os links simbólicos de uma -llistagem ong como esta, faça uma alteração simples:

LS_COLORS='rs=:no=//:lc=:rc=:ec=/ :'$(
set -- di fi mh pi so do bd cd or su sg ca tw ow st ex
for fc do printf %s "$fc=$fc/:"
done)ln=///: ls -l --color=always | sed ':ln
\|///|{N;\|\n//|!bln};s|.*//||'

Meus resultados após a execução se parecem com ...

total 884
-rw-r--r-- 1 mikeserv mikeserv    793 Jul  9 11:23 fi/1/ 
drwxr-xr-x 1 mikeserv mikeserv    574 Jun 24 16:50 di/Desktop/ /
-rw-r--r-- 1 mikeserv mikeserv    166 Jul  4 23:02 fi/Terminology.log/ 
-rw-r--r-- 1 mikeserv mikeserv      0 Jul  6 11:24 fi/new
file/ 
-rwxr-xr-x 1 mikeserv mikeserv    190 Jun 22 11:26 ex/script.sh/ *
-rw-r--r-- 1 mikeserv mikeserv 433568 Jun 22 17:10 fi/shot-2014-06-22_17-10-16.jpg/ 
-rw-r--r-- 1 mikeserv mikeserv     68 Jun 17 19:59 fi/target.txt/ 

Usando algum comando como o que eu faço acima:

LSCOLORS=...$(...)fc1=///:fc2=///: ls ... | sed ...

... (onde fc1e fc2são os tipos de arquivos listados depois set --no subshell) deve servir para remover com segurança quaisquer combinações de tipos de arquivos que você possa desejar da lssaída, independentemente de qualquer caractere que os nomes de arquivos possam conter.

mikeserv
fonte
Heh, legal, +1 pela originalidade. No entanto, parece que os diretórios estão bloqueados, eles são impressos com um código de cores ANSI ilegível. Enfim, você poderia adicionar uma explicação de como isso funciona? Talvez explicar o que lc, nc, etc, são?
terdon
@terdon - Eles são impressos apenas com as fugas ANSI já definidas em seu ambiente e ou compiladas como padrão para lso di=valor. Você precisaria adicionar di=:ao var para anular isso - e para todos os outros. É o que quero dizer com API - você endereça cada tipo de arquivo, mas é necessário endereçar a mesma interface em todos os casos. Portanto, definir lnuma maneira e ignorar os dipadrões não funcionará. Existem muitos padrões compilados também ... Hmm. Sua pergunta sobre lcand ncand stuff é válida - completamente - eu já fiz isso outro dia. Vou ligar, acho.
117614
Ai, então eu teria que especificar todos os tipos de arquivos possíveis sempre ou verificar o que está presente antes da mão? OK obrigado.
terdon
@terdon - veja minha edição agora - ele faz tudo em algumas linhas de shell, especificando cada tipo por seu nome e delimitando de forma confiável todos os nomes de arquivos. Também não há imprimíveis. E há uma API para isso de qualquer maneira - é dircolors. Ele lê um arquivo de configuração e gera um evalvalor var amigável para o shell . É bem fácil - mas também são poucas as linhas lá em cima. De qualquer forma, se você quiser usá- dircolorslo, salve dois arquivos diferentes e invoque-o dessa maneira. Além disso, mostrei a você outro dia como fazer as duas coisas ao mesmo tempo com \0delimitadores NUL como parte de cada sequência de escape.
mikeserv
@terdon - Tentei tornar mais claro o quão fácil é para definir todos os valores que podem ser compilados em - Acho que tenho em tudo, talvez demasiado muitos ... LS_COLORS='rs=:no=\0//:lc=:rc=:ec=\0//:'$( set -- di fi ln mh pi so do bd cd or su sg ca tw ow st ex; for fc do printf %s "$fc=/$fc//\0:"; done) ls -l --color=always | cat -A
mikeserv
0

A resposta acima leva ao seguinte:

 ls -1F | grep -i "/"

Curiosamente, deixar o 1 fora do comando ls ainda fornece uma lista.

woodsjay
fonte
11
Por que grep -i? Não é como se /pudesse ser maiúsculo ou minúsculo. lsassume o formato de coluna noe quando a saída não é um terminal (é um tubo aqui).
Kusalananda
-2

Tente este:

ls | grep -v " -> "
csny
fonte
2
Isso falhará por vários motivos, por exemplo, em um arquivo chamado a -> b. Ele também falhará nos nomes de arquivos com novas linhas e outras estranhezas. Leia isto para saber por que analisar ls é uma má ideia.
terdon
@terdon Existem razões mais fundamentais para isso falhar. Ou lsé um alias ls -l, e isso retorna colunas antes do nome do arquivo, ou não é, e isso não faz nada de especial nos links simbólicos.
Gilles 'SO- stop be evil' ''
@terdon - lsanalisa a si próprio. todos analisamos lstoda vez que imprimimos em um terminal - há uma API . Publiquei uma resposta demonstrando algo parecido aqui - mas você pode fazer muito mais. Eu mostrei a você outro dia - você mesmo tinha nomes de arquivo delimitados por nulo.
mikeserv
@ MikeServ Eu sei, eu sei. No entanto, explicar como fazer isso com segurança leva várias centenas de linhas, como você mesmo demonstrou. Tal como está, essa resposta sofre com todos os perigos de analisar sl e o OP claramente não está ciente deles.
terdon
@terdon - lsnão vai comer seus filhos nem nada - é apenas um programa. Se você não explicar, serão necessárias várias centenas para explicar e várias centenas para explicar por que as várias centenas de linhas às quais você acabou de vincular estão erradas. E além disso - são apenas duas linhas:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Tente este comando: ls -p | grep -v @

Thushi
fonte
Não, lista os links.
TheMeaningfulEngineer
@Alan: k experimente isso #find -type f
Thushi
11
-1 porque: i) O -pjust adiciona uma barra aos diretórios, você está procurando -Fii) sua findalternativa não listará os diretórios e iii) Teste suas respostas antes de postar.
terdon