ls contenu d'un répertoire ignorant les liens symboliques

17

J'ai un répertoire dans lequel j'aimerais lister tout le contenu (fichiers et sous répertoires) sans afficher les liens symboliques. J'utilise des utilitaires GNU sous Linux. La lsversion est 8.13.

Exemple:

Liste complète des répertoires:

~/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

Ce que j'aimerais obtenir

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

NOTE: Ma principale motivation est de faire un grep récursif (GNU grep 2.10) sans suivre les liens symboliques.

TheMeaningfulEngineer
la source

Réponses:

28

Pour la question posée, vous pouvez utiliser find:

find . -mindepth 1 ! -type l

répertorie tous les fichiers et répertoires du répertoire actuel ou de tout sous-répertoire qui ne sont pas des liens symboliques.

mindepth 1est juste de sauter l' .entrée du répertoire en cours. La viande de celui-ci est la combinaison de -type l, ce qui signifie "est un lien symbolique", et !, ce qui signifie nier le test suivant. En combinaison, ils correspondent à chaque fichier qui n'est pas un lien symbolique. Cela répertorie tous les fichiers et répertoires de manière récursive, mais pas de liens symboliques.

Si vous voulez juste des fichiers normaux (et non des répertoires):

find . -type f

Pour inclure uniquement les enfants directs de ce répertoire, et pas tous les autres de manière récursive:

find . -mindepth 1 -maxdepth 1

Vous pouvez combiner ces tests (et d'autres) ensemble pour obtenir la liste des fichiers que vous souhaitez.

Pour exécuter un détail grepsur chaque fichier correspondant aux tests que vous utilisez, utilisez -exec:

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

Le '{}'sera remplacé par les fichiers. Il +est nécessaire de dire que findvotre commande est exécutée. L'option -Hforce grep à afficher un nom de fichier même s'il se trouve qu'il s'exécute avec un seul fichier correspondant.

Michael Homer
la source
Il ne montre pas les fichiers dans lesquels le motif est trouvé.
TheMeaningfulEngineer
C'est pour cela que vous courez grep(voir dernier paragraphe).
Michael Homer
Je ne travaille pas pour moi. Il sort juste les modèles trouvés sans nom de fichier où il a été trouvé.
TheMeaningfulEngineer
2
Utilisez-le grep -H, si vous l'avez (voir man grep), ou autrement grep 'pat' '{}' /dev/null ';'pour le faire croire qu'il existe plusieurs fichiers si vous ne l'avez pas.
Michael Homer
1
+ne répond pas à cette exigence dans le cas dégénéré où il ne reste qu'un seul fichier ou un seul fichier à la fin.
Michael Homer
17

Ou, plus simple:

ls -l | grep -v ^l

Explication

ls -lsignifie lister sous forme longue . Lorsque vous faites cela, la première chaîne de caractères donne des informations sur chaque fichier. Le premier caractère indique le type de chaque fichier. S'il s'agit d'un lien symbolique, an lest le premier caractère.

grep -v ^lsignifie filtrer ( -v) les lignes commençant par ( ^) an l.

Jonathan Petitcolas
la source
Cela fonctionne très bien, mais comment obtenir uniquement les noms de dossier? Je veux l'utiliser dansfor i in
Luka
Pouvez-vous expliquer ce code s'il vous plaît?
abg
Attention que cette solution ne fonctionne pas si vous OMMISSIONS -ldans ls. Donc, cela ne fonctionnerait pas comme prévu:ls | grep -v ^l
stamster
@Luka Vous ne bouclez pas sur la sortie de find, voir unix.stackexchange.com/questions/321697
Kusalananda
1
@abg - J'ai ajouté une explication. J'espère que cela aide.
Geoff
7

À partir de la version 2.12, l' -roption pour GNU grep ne déréférence pas les liens symboliques, sauf si vous les spécifiez à la main:

-r, - récursif

Lisez tous les fichiers de chaque répertoire, récursivement, en suivant les liens symboliques uniquement s'ils se trouvent sur la ligne de commande. Cela équivaut à l'option -d recurse.

-R, --dereference-récursif

Lisez tous les fichiers sous chaque répertoire, récursivement. Suivez tous les liens symboliques, contrairement à -r.

tijagi
la source
Quelle version de grep est-ce? J'ai -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.en 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
tijagi
5

En zsh, ce serait facile grâce aux qualificatifs glob :

grep -- PATTERN **/*(.)

Le modèle **/parcourt les sous-répertoires récursivement. Le qualificatif glob .restreint la correspondance aux fichiers standard.

Sans zsh, utilisez find(voir la réponse de Michael Horner ). Et dans ce cas particulier, GNU grep peut faire ce que vous voulez (c'est exactement ce qui grep -rfait) - mais seulement depuis la version 2.12, les versions antérieures suivaient des liens symboliques.

Gilles 'SO- arrête d'être méchant'
la source
2

Vous pouvez utiliser $LS_COLORSpour ce faire. Si votre version de lsprend en charge la spécification des couleurs à l'aide de cette variable, vous pouvez définir la sortie par type de fichier. C'est un comportement intégré et très configurable. J'ai donc créé quelques fichiers pour faire une démonstration comme ceci:

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

Alors maintenant je vais faire:

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
...

Et les nuls sont là aussi ...

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$
...

Vous pouvez spécifier pour tout ou partie des types de fichiers. Le faire pour un seul type de fichier peut ne pas vous donner envie, car il lsa incorporé certaines valeurs de compilation par défaut pour les échappements de terminal. Vous feriez beaucoup mieux d'adresser l'API comme une interface unique. Voici un petit moyen simple d'analyser et d'attribuer les dircolorsparamètres par défaut de l' environnement actuel :

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

Sa sortie dans mon répertoire personnel ressemble à ceci:

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/

Vous pouvez également exécuter cela avec cat -Aet la seule différence que vous rencontrerez est que vous verrez $pour les nouvelles lignes - il n'y a pas de caractères non imprimables introduits par ls --color=alwaysavec cette configuration - seulement ce que vous voyez ici.

ls insère ses sorties de terminal par défaut comme ceci:

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

... où les valeurs par défaut pour $lc (à gauche du code) , $rc (à droite du code) et $rs (réinitialiser) sont:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...respectivement. ${type_code}est utilisé pour remplacer les divers fi (fichier normal - non défini par défaut) , di (répertoire) , ln (lien) et tous les autres types de fichiers que je connais. Il y a aussi $no (normal) qui est aussi par défaut non défini et qui est ici représenté par //le début de chaque ligne. Mon petit IFS=:bloc simple fonctionne simplement en insérant le nom de chaque configurable en tant que sa propre valeur et en ajoutant une barre oblique ou deux - bien que les \0octets NUL feraient aussi bien.

Par défaut, une autre lssera $rsimmédiatement insérée immédiatement avant sa première sortie $lc, mais elle n'est pas représentée avec précision ici. Dans ce cas, j'ai spécifié $ec (code de fin) qui remplace dans $rstous les cas - lorsqu'il est spécifié, vous n'obtenez pas de supplément $rsentre $noet ${type_code}comme vous le feriez autrement - il ne présente que immédiatement après un nom de fichier et une fois au début de la sortie - comme vous pouvez le voir dans la barre oblique supplémentaire en tête de la première ligne.

Voici un extrait du mien $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:...

Et, en vérité, mon petit hack shell est probablement trop compliqué - il existe une interface largement disponible pour attribuer ces valeurs. Essayez dircolors -pdans votre cli et info dircolorspour plus d'informations à ce sujet.

Vous pouvez encapsuler les noms de fichiers dans des chaînes arbitraires. Vous pouvez les commenter si vous le souhaitez. Vous pouvez spécifier des comportements similaires basés uniquement sur l'extension de fichier. Il n'y a vraiment pas beaucoup de choses que vous ne pouvez pas spécifier de cette façon.

Maintenant, je n'invente pas tout cela non plus - je l'ai appris après être tombé sur le code source par accident.

Avec cette configuration particulière lsémettra:

  1. $no - une fois par enregistrement au début de chaque enregistrement

  2. ${type_code} - une fois immédiatement avant chaque nom de fichier pour inclure une abréviation du type du fichier et toujours sur la même ligne que et 7 champs délimités par des espaces après $no ou immédiatement après une ->dénotation de cible d'un lien symbolique.

  3. $ec - une fois immédiatement avant la toute première ligne et ensuite une seule fois immédiatement après chaque nom de fichier.

  4. Toutes les autres valeurs sont vides.

Ce qui suit est un nombre nul ls, et cette fois je vais utilisercat -A , mais sans lui, il ressemblerait au dernier exemple:

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^@//$

Et donc pour supprimer de manière fiable tous les liens symboliques d'un-l liste ong comme celle-ci, vous pouvez apporter une modification simple:

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|.*//||'

Mes résultats après la course ressemblent à ...

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/ 

En utilisant une commande comme celle que je fais ci-dessus:

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

... (où fc1et fc2sont les types de fichiers répertoriés après set --dans le sous-shell) devrait servir à supprimer de manière fiable toutes les combinaisons de types de fichiers que vous pourriez souhaiter de la lssortie, quels que soient les caractères que les noms de fichiers peuvent contenir.

mikeserv
la source
Hé, sympa, +1 pour l'originalité. Semble toutefois s'étouffer avec les répertoires, ils sont imprimés avec un code couleur ANSI tronqué. Quoi qu'il en soit, pourriez-vous ajouter une explication sur la façon dont cela fonctionne? Expliquer peut - être ce que lc, nc, etc sont?
terdon
@terdon - Ils sont uniquement imprimés avec les échappements ANSI déjà définis dans votre environnement et ou compilés par défaut pour lsla di=valeur de. Vous auriez besoin d'ajouter di=:à la var pour annuler cela - et pour tous les autres. C'est ce que je veux dire par l'API - vous vous adressez à chaque type de fichier mais vous devez adresser la même interface dans tous les cas. Donc, définir lnun sens et ignorer les divaleurs par défaut ne fonctionnera pas. Il y a aussi beaucoup de valeurs par défaut compilées ... Hmm. Votre question sur lcet ncet les choses sont valables - complètement - je l'ai déjà fait l'autre jour. Je vais le lier, je pense.
mikeserv
Aïe, je devrais donc toujours spécifier tous les types de fichiers possibles ou vérifier ce qui est présent à l'avance? OK merci.
terdon
@terdon - voir ma modification tout à l'heure - il fait tout cela dans quelques lignes de shell, spécifiant chaque type par son nom de type et délimitant de manière fiable chaque nom de fichier. Il n'y a pas non plus non imprimable introduit. Et il y a une API pour ça de toute façon - ça l'est dircolors. Il lit un fichier de configuration et génère une evalvaleur var adaptée au shell . C'est assez facile - mais les quelques lignes sont aussi là-haut. Quoi qu'il en soit, si vous souhaitez l'utiliser, dircolorsenregistrez simplement deux fichiers différents et invoquez-le de cette façon. De plus, je vous ai montré l'autre jour comment faire les deux à la fois avec des \0délimiteurs NUL dans le cadre de chaque séquence d'échappement.
mikeserv
@terdon - J'ai essayé de préciser à quel point il est facile de définir toutes les valeurs qui pourraient être compilées - Je pense que je les ai toutes, peut-être trop ... 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

La réponse ci-dessus conduit à ce qui suit:

 ls -1F | grep -i "/"

Curieusement, le fait de laisser le 1 hors de la commande ls donne toujours une liste.

woodsjay
la source
1
Pourquoi grep -i? Ce n'est pas comme si elle /pouvait être en majuscules ou en minuscules. lspar défaut au format noe-column lorsque la sortie n'est pas un terminal (c'est un pipe ici).
Kusalananda
-2

Essaye celui-là:

ls | grep -v " -> "
csny
la source
2
Cela échouera pour diverses raisons, par exemple sur un fichier appelé a -> b. Il échouera également sur les noms de fichiers avec des sauts de ligne et d'autres étrangetés. Veuillez lire ceci pour savoir pourquoi analyser ls est une mauvaise idée.
terdon
@terdon Il y a des raisons plus fondamentales pour lesquelles cela échouera. Soit lsest aliasé ls -l, et cela renvoie des colonnes avant le nom du fichier, soit il ne l'est pas, et cela n'a rien de spécial à propos des liens symboliques.
Gilles 'SO- arrête d'être méchant'
@terdon - s'analyse ls. nous analysons lstous chaque fois que nous imprimons sur un terminal - il y a un api . J'ai posté une réponse démontrant quelque chose comme ça ici - mais vous pouvez faire beaucoup plus. Je vous ai montré l'autre jour - vous aviez vous-même des noms de fichiers délimités par des valeurs nulles.
mikeserv
@mikeserv Je sais, je sais. Cependant, expliquer comment le faire en toute sécurité prend plusieurs centaines de lignes, comme vous l'avez vous-même démontré. En l'état, cette réponse souffre de tous les dangers de l'analyse syntaxique et l'OP n'en est clairement pas conscient.
terdon
@terdon - lsne va pas manger vos enfants ou quoi que ce soit - c'est seulement un programme. Si vous ne l'expliquez pas, il faudra plusieurs centaines pour l'expliquer et plusieurs centaines de plus pour expliquer pourquoi les plusieurs centaines de lignes que vous venez de relier sont fausses. Et en plus - ce ne sont que deux lignes:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Essayez cette commande: ls -p | grep -v @

Thushi
la source
Non, liste les liens.
TheMeaningfulEngineer
@Alan: k essayez cecifind -type f
Thushi
1
-1 car: i) Le -pajoute simplement une barre oblique aux répertoires, vous recherchez -Fii) votre findalternative ne listera pas les répertoires et iii) Veuillez tester vos réponses avant de poster.
terdon