Der Inhalt eines Verzeichnisses ignoriert Symlinks

16

Ich habe ein Verzeichnis, in dem ich den gesamten Inhalt (Dateien und Unterverzeichnisse) auflisten möchte, ohne die symbolischen Links anzuzeigen. Ich benutze GNU-Dienstprogramme unter Linux. Die lsVersion ist 8.13.

Beispiel:

Vollständige Verzeichnisliste:

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

Was ich gerne bekommen würde

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

HINWEIS: Meine Hauptmotivation ist es, ein rekursives grep (GNU grep 2.10) zu machen, ohne Symlinks zu folgen.

TheMeaningfulEngineer
quelle

Antworten:

28

Für die angegebene Frage können Sie verwenden find:

find . -mindepth 1 ! -type l

listet alle Dateien und Verzeichnisse im aktuellen Verzeichnis oder allen Unterverzeichnissen auf, die keine Symlinks sind.

mindepth 1ist nur der .aktuelle Verzeichniseintrag zu überspringen . Das Fleisch davon ist die Kombination von -type l, was "ist eine symbolische Verbindung" bedeutet, und !was bedeutet, den folgenden Test zu negieren. In Kombination stimmen sie mit jeder Datei überein, die kein Symlink ist. Dies listet alle Dateien und Verzeichnisse rekursiv auf, aber keine Symlinks.

Wenn Sie nur normale Dateien (und keine Verzeichnisse) möchten:

find . -type f

So schließen Sie nur die direkten untergeordneten Elemente dieses Verzeichnisses und nicht alle anderen rekursiv ein:

find . -mindepth 1 -maxdepth 1

Sie können diese (und andere) Tests kombinieren, um die Liste der gewünschten Dateien zu erhalten.

grepVerwenden Sie zum Ausführen einer bestimmten Datei, die den von Ihnen verwendeten Tests entspricht, Folgendes -exec:

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

Das '{}'wird durch die Dateien ersetzt. Das +ist notwendig, um mitzuteilen, dass findIhr Befehl ausgeführt wurde. Die Option -Herzwingt, dass grep einen Dateinamen anzeigt, auch wenn er mit einer einzelnen übereinstimmenden Datei ausgeführt wird.

Michael Homer
quelle
Es werden nicht die Dateien angezeigt, in denen das Muster gefunden wurde.
TheMeaningfulEngineer
Dafür laufen Sie grep(siehe letzter Absatz).
Michael Homer
Ich arbeite nicht für mich. Es werden nur die gefundenen Muster ohne Dateinamen ausgegeben, in denen sie gefunden wurden.
TheMeaningfulEngineer
2
Verwenden Sie grep -H, wenn Sie es haben (siehe man grep) oder auf andere Weise grep 'pat' '{}' /dev/null ';', um sich vorzustellen, dass es mehrere Dateien gibt, wenn Sie dies nicht tun.
Michael Homer
1
+wird dieser Anforderung in dem entarteten Fall nicht gerecht, in dem am Ende nur noch eine Datei oder eine Datei übrig ist.
Michael Homer
15

Oder einfacher:

ls -l | grep -v ^l

Erläuterung

ls -lbedeutet, in langer Form aufzulisten . In diesem Fall enthält die erste Zeichenfolge Informationen zu jeder Datei. Das erste Zeichen gibt an, um welchen Dateityp es sich handelt. Wenn es sich um einen Symlink handelt, list an das erste Zeichen.

grep -v ^lbedeutet, -vdie Zeilen herauszufiltern , die mit ( ^) beginnen l.

Jonathan Petitcolas
quelle
Das funktioniert prima, aber wie bekommt man nur Ordnernamen? Ich möchte das infor i in
Luka
Können Sie diesen Code bitte erklären?
Abg
Beachten Sie, dass diese Lösung nicht , wenn Sie ommit funktioniert -lin ls. Dies würde also nicht wie erwartet funktionieren:ls | grep -v ^l
Stamster
@ Luka Die Ausgabe von findwird nicht durchlaufen
Kusalananda
1
@abg - Ich habe eine Erklärung hinzugefügt. Ich hoffe es hilft.
Geoff
7

Ab Version 2.12 -rdereferenziert die Option für GNU grep keine symbolischen Links mehr, es sei denn, Sie geben sie manuell an:

-r, --rekursiv

Lesen Sie alle Dateien in jedem Verzeichnis rekursiv und folgen Sie symbolischen Verknüpfungen nur, wenn sie sich in der Befehlszeile befinden. Dies entspricht der Option -d recurse.

-R, --dereferenz-rekursiv

Lesen Sie alle Dateien in jedem Verzeichnis rekursiv. Befolgen Sie im Gegensatz zu -r alle symbolischen Links.

tijagi
quelle
Welche Version von grep ist das? Ich habe -R, -r, --recursive Read all files under each directory, recursively; this is equivalent to the -d recurse option.in 2.10
TheMeaningfulEngineer
grep (GNU grep) 2.14
tijagi
5

In zsh wäre dies dank glob-Qualifikatoren einfach :

grep -- PATTERN **/*(.)

Das Muster **/durchläuft Unterverzeichnisse rekursiv. Das Glob-Qualifikationsmerkmal .schränkt den Abgleich mit regulären Dateien ein.

Verwenden Sie ohne zsh find(siehe Antwort von Michael Horner ). Und in diesem speziellen Fall kann GNU grep tun, was Sie wollen (genau das grep -rtut es) - aber erst seit Version 2.12 folgten frühere Versionen symbolischen Links.

Gilles 'SO - hör auf böse zu sein'
quelle
2

Sie können dazu verwenden $LS_COLORS. Wenn Ihre Version von lsdie Angabe der Farben mithilfe dieser Variablen unterstützt, können Sie die Ausgabe pro Dateityp definieren. Es ist eingebautes Verhalten und sehr konfigurierbar. Deshalb habe ich einige Dateien erstellt, um dies zu demonstrieren:

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

Also mache ich jetzt:

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

Und die Nullen sind auch da ...

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

Sie können für alle oder beliebige Dateitypen angeben. Wenn Sie dies nur für einen Dateityp tun, erhalten Sie möglicherweise keine Ergebnisse, da lseinige standardmäßige Kompilierungswerte für Terminal-Escapes enthalten sind. Es ist viel besser, die API als einzelne Schnittstelle zu adressieren. Im Folgenden finden Sie eine einfache Methode zum Parsen und Zuweisen der Standardeinstellungen für die aktuelle Umgebung dircolors:

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

Die Ausgabe in meinem Home-Verzeichnis sieht folgendermaßen aus:

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/

Sie können das auch ausführen, cat -Aund der einzige Unterschied besteht darin, dass Sie $für Zeilenumbrüche sehen - ls --color=alwaysmit dieser Konfiguration werden keine nicht druckbaren Zeichen eingeführt - nur das, was Sie hier sehen.

ls fügt sein Standardterminal so ein:

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

... wobei die Standardwerte für $lc (links vom Code) , $rc (rechts vom Code) und $rs (zurückgesetzt) sind:

 \033 - ESCAPE
 m - END ESCAPE
 0 - reset 

...beziehungsweise. ${type_code}wird verwendet, um für die verschiedenen fi (reguläre Datei - Standardeinstellung nicht festgelegt) , di (Verzeichnis) , ln (Verknüpfung) und jeden anderen mir bekannten Dateityp zu stehen. Es gibt auch $no (normales), das ebenfalls standardmäßig nicht gesetzt ist und das hier //am Anfang jeder Zeile dargestellt wird. Mein einfacher kleiner IFS=:Block fügt einfach den Namen für jedes konfigurierbare Element als eigenen Wert ein und fügt ein oder zwei Schrägstriche hinzu - obwohl auch \0NUL-Bytes ausreichen würden.

Standardmäßig lswird auch eine $rsunmittelbar vor der ersten Ausgabe eingefügt $lc- dies wird hier jedoch nicht genau dargestellt. In diesem Fall habe ich $ec (Endcode) angegeben, der $rsin allen Fällen für steht - wenn es angegeben ist, erhalten Sie $rszwischen $nound ${type_code}wie sonst auch kein Extra - es wird nur unmittelbar nach einem Dateinamen und einmal am Anfang der Ausgabe angezeigt - Wie Sie in dem einen zusätzlichen Schrägstrich am Anfang der ersten Zeile sehen können.

Hier ist ein Ausschnitt von mir $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:...

Und in Wahrheit ist mein kleiner Shell-Hack wahrscheinlich zu kompliziert - es gibt eine weithin verfügbare Schnittstelle, um diese Werte zuzuweisen. Versuchen Sie es dircolors -pin Ihrem CLI und info dircolorsfür weitere Informationen dazu.

Sie können die Dateinamen in beliebige Zeichenfolgen einschließen. Sie können sie auskommentieren, wenn Sie möchten. Sie können ähnliche Verhaltensweisen nur basierend auf der Dateierweiterung angeben. Es gibt wirklich nicht viel, was man so nicht spezifizieren kann.

Jetzt denke ich mir auch nicht nur das alles aus - ich habe es erfahren, nachdem ich versehentlich über den Quellcode gestolpert bin .

Mit dieser speziellen Konfiguration lswird Folgendes ausgegeben:

  1. $no - Einmal pro Datensatz am Anfang jedes Datensatzes

  2. ${type_code}- Einmal unmittelbar vor jedem Dateinamen, um eine Abkürzung des Dateityps einzufügen, die immer in der gleichen Zeile wie und in 7 durch Leerzeichen getrennten Feldern nach $no oder unmittelbar nach ->dem Ziel eines symbolischen Links vorkommt.

  3. $ec - einmal unmittelbar vor der ersten Zeile und danach nur einmal unmittelbar nach jedem Dateinamen.

  4. Alle anderen Werte sind leer.

Was folgt, ist eine durch Nullen getrennte Zeichenfolge ls, und dieses Mal werde ich sie verwenden cat -A. Ohne diese Zeichenfolge würde sie jedoch genauso aussehen wie das letzte Beispiel:

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

Um alle symbolischen Links aus einer -lsolchen Liste zuverlässig zu entfernen , können Sie eine einfache Änderung vornehmen:

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

Meine Ergebnisse nach dem Laufen sehen so aus ...

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/ 

Verwenden Sie einen Befehl wie den oben beschriebenen:

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

... (wo fc1und fc2welche Dateitypen nach set --in der Subshell aufgelistet sind) sollten dazu dienen, Kombinationen von Dateitypen, die Sie möchten, zuverlässig aus der lsAusgabe zu entfernen, unabhängig davon, welche Zeichen die Dateinamen enthalten könnten.

mikeserv
quelle
He, nett, +1 für Originalität. Scheint jedoch an Verzeichnissen zu ersticken, sie werden mit einem verstümmelten ANSI-Farbcode gedruckt. Wie auch immer, könnten Sie eine Erklärung hinzufügen, wie das funktioniert? Vielleicht erklären , was lc, ncetc sind?
Terdon
@terdon - Sie werden nur mit den ANSI-Escapes gedruckt, die bereits in Ihrer Umgebung festgelegt und als Standard für lsden di=Wert von 's kompiliert wurden . Sie müssten di=:die Variable ergänzen, um dies zu annullieren - und für alle anderen. Das meine ich mit der API - Sie adressieren jeden Dateityp, aber Sie müssen die Schnittstelle in allen Fällen gleich adressieren. Das Festlegen lneiner Richtung und das Ignorieren der diStandardeinstellungen funktioniert also nicht. Es gibt auch viele Standardeinstellungen ... Hmm. Deine Fragen zu lcund ncund so sind gültig - völlig - ich habe es aber schon neulich getan. Ich werde es verknüpfen, denke ich.
mikeserv
Autsch, also müsste ich entweder immer alle möglichen Dateitypen angeben oder vorher nachsehen, was vorhanden ist? OK danke.
terdon
@terdon - siehe meine Bearbeitung gerade - alles in ein paar Shell-Zeilen, wobei jeder Typ durch seinen Typnamen angegeben wird und jeder Dateiname zuverlässig abgegrenzt wird. Es werden auch keine nicht druckbaren Elemente eingeführt. Und dafür gibt es sowieso eine API - es ist dircolors. Es liest eine Konfigurationsdatei und gibt einen Shell- evalfreundlichen var-Wert aus. Es ist ziemlich einfach - aber die wenigen Angestellten dort oben sind es auch. Wie auch immer, wenn Sie verwenden möchten, dircolorsspeichern Sie einfach zwei verschiedene Dateien und rufen Sie sie auf diese Weise auf. Außerdem habe ich Ihnen \0neulich gezeigt, wie Sie bei jeder Escape-Sequenz beides gleichzeitig mit NUL-Begrenzern tun können .
mikeserv
@terdon - Ich habe versucht zu verdeutlichen, wie einfach es ist, alle Werte festzulegen, in denen kompiliert werden könnte. Ich glaube, ich habe alle, vielleicht zu viele ... 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

Die Antwort oben führte zu Folgendem:

 ls -1F | grep -i "/"

Seltsamerweise gibt das Auslassen der 1 aus dem ls-Befehl immer noch eine Liste.

Woodsjay
quelle
1
Warum grep -i? Es ist nicht so, als ob /Groß- oder Kleinschreibung möglich wäre. lsStandardmäßig wird ein Noe-Spaltenformat verwendet, wenn die Ausgabe kein Terminal ist (hier ist es eine Pipe).
Kusalananda
-2

Probier diese:

ls | grep -v " -> "
csny
quelle
2
Dies schlägt aus verschiedenen Gründen fehl, beispielsweise bei einer aufgerufenen Datei a -> b. Es wird auch bei Dateinamen mit Zeilenumbrüchen und anderen Seltsamkeiten fehlschlagen. Bitte lesen Sie dies, um herauszufinden, warum das Parsen von ls eine schlechte Idee ist.
terdon
@terdon Es gibt grundlegendere Gründe, warum dies fehlschlagen wird. Entweder lsist es ein Alias ​​für ls -l, und dies gibt Spalten vor dem Dateinamen zurück, oder es ist nicht so, und dies macht nichts Besonderes in Bezug auf symbolische Links.
Gilles 'SO- hör auf böse zu sein'
@terdon - lsanalysiert sich. Wir alle analysieren lsjedes Mal, wenn wir auf ein Terminal drucken - es gibt eine API . Ich habe hier eine Antwort gepostet, die so etwas demonstriert - aber Sie können noch viel mehr tun. Ich habe es dir neulich gezeigt - du hattest selbst Dateinamen ohne Trennzeichen.
mikeserv
@mikeserv Ich weiß, ich weiß. Wie Sie selbst gezeigt haben, dauert es jedoch mehrere hundert Zeilen, bis Sie erklärt haben, wie dies sicher geht. Gegenwärtig ist diese Antwort mit allen Gefahren des Parsens behaftet, und dem OP sind diese eindeutig nicht bekannt.
terdon
@terdon - lsisst nicht deine Kinder oder so was - es ist nur ein Programm. Wenn Sie erklären es nicht dann es wird dauern mehrere hundert , es zu erklären und mehrere hundert mehr zu erklären , warum die mehrere hundert Zeilen , die Sie gerade verbunden sind falsch. Und außerdem - das sind nur zwei Zeilen:LS_COLORS='lc=:rc=:ec=:ln=\n\n\0HERE_THERE_BE_A_LINK>>\0:' \ ls -1 --color=always | cat
mikeserv
-3

Versuchen Sie diesen Befehl: ls -p | grep -v @

Thushi
quelle
Nein, listet die Links auf.
TheMeaningfulEngineer
@ Alan: k versuchen Sie diesfind -type f
Thushi
1
-1 weil: i) Das -pfügt nur einen Schrägstrich zu Verzeichnissen hinzu, Sie suchen -Fii) Ihre findAlternative listet keine Verzeichnisse auf und iii) Bitte testen Sie Ihre Antworten vor dem Posten.
Terdon