Ich versuche, eine Bash-Shell-Funktion zu schreiben, mit der ich doppelte Kopien von Verzeichnissen aus meiner Umgebungsvariablen PATH entfernen kann.
Mir wurde gesagt, dass es möglich ist, dies mit einem einzeiligen Befehl mit dem awk
Befehl zu erreichen, aber ich kann nicht herausfinden, wie es geht. Weiß jemand wie?
Antworten:
Wenn Sie noch keine Duplikate im haben
PATH
und nur Verzeichnisse hinzufügen möchten, wenn diese noch nicht vorhanden sind, können Sie dies problemlos mit der Shell alleine tun.Und hier ist ein Shell-Snippet, aus dem Duplikate entfernt werden
$PATH
. Es geht die Einträge nacheinander durch und kopiert diejenigen, die noch nicht gesehen wurden.quelle
PATH=$PATH:x=b
Das x im ursprünglichen PATH hat möglicherweise den Wert a. Wenn Sie also in der angegebenen Reihenfolge iterieren, wird der neue Wert ignoriert, aber in umgekehrter Reihenfolge der neue Wert wird wirksam.PATH=x:$PATH
.PATH=$PATH:...
nichtPATH=...:$PATH
. Daher ist es sinnvoller, die umgekehrte Reihenfolge zu wiederholen. Auch wenn Ihr Weg auch funktionieren würde, hängen die Leute auf umgekehrte Weise an.Hier ist eine verständliche einzeilige Lösung, die alles richtig macht: Entfernt Duplikate, behält die Reihenfolge der Pfade bei und fügt am Ende keinen Doppelpunkt hinzu. Sie sollten also einen deduplizierten Pfad erhalten, der genau dasselbe Verhalten wie das Original aufweist:
Es wird einfach auf Doppelpunkt (
split(/:/, $ENV{PATH})
) aufgeteilt, verwendetgrep { not $seen{$_}++ }
, um alle wiederholten Instanzen von Pfaden mit Ausnahme des ersten Vorkommens herauszufiltern, und fügt dann die verbleibenden durch Doppelpunkte getrennt wieder zusammen und gibt das Ergebnis aus (print join(":", ...)
).Wenn Sie mehr Struktur benötigen und auch die Möglichkeit haben möchten, andere Variablen zu deduplizieren, probieren Sie dieses Snippet aus, das ich derzeit in meiner eigenen Konfiguration verwende:
Dieser Code dedupliziert sowohl PATH als auch MANPATH, und Sie können problemlos
dedup_pathvar
andere Variablen aufrufen , die durch Doppelpunkte getrennte Pfadlisten enthalten (z. B. PYTHONPATH).quelle
chomp
, um eine nachgestellte Zeile zu entfernen. Das hat bei mir funktioniert:perl -ne 'chomp; print join(":", grep { !$seen{$_}++ } split(/:/))' <<<"$PATH"
Hier ist eine schlanke:
Länger (um zu sehen, wie es funktioniert):
Ok, da du neu in Linux bist, ist hier, wie man PATH tatsächlich ohne ein abschließendes ":" setzt.
Übrigens, stellen Sie sicher, dass Sie KEINE Verzeichnisse mit ":" in Ihrem PFAD haben, sonst wird es vermasselt.
Ein Verdienst für:
quelle
echo -n
. Ihre Befehle scheinen nicht mit "here strings" zu funktionieren, zB try:awk -v RS=: -v ORS=: '!arr[$0]++' <<< ".:/foo/bin:/bar/bin:/foo/bin"
Hier ist ein AWK One Liner.
wo:
printf %s "$PATH"
druckt den Inhalt$PATH
ohne nachfolgende Newline ausRS=:
Ändert das Begrenzungszeichen des Eingabesatzes (Standard ist Newline)ORS=
Ändert den Begrenzer des Ausgabesatzes in die leere Zeichenfolgea
der Name eines implizit erstellten Arrays$0
verweist auf den aktuellen Datensatza[$0]
ist eine assoziative Array-Dereferenzierung++
ist der Post-Inkrement-Operator!a[$0]++
schützt die rechte Seite, dh es wird sichergestellt, dass der aktuelle Datensatz nur gedruckt wird, wenn er zuvor nicht gedruckt wurdeNR
Die aktuelle Datensatznummer, beginnend mit 1Dies bedeutet, dass AWK verwendet wird, um den
PATH
Inhalt entlang der:
Trennzeichen aufzuteilen und doppelte Einträge herauszufiltern, ohne die Reihenfolge zu ändern.Da assoziative AWK-Arrays als Hash-Tabellen implementiert sind, ist die Laufzeit linear (dh in O (n)).
Beachten Sie, dass wir nicht nach Anführungszeichen suchen müssen,
:
da Shells keine Anführungszeichen enthalten , um Verzeichnisse zu unterstützen,:
deren Name in derPATH
Variablen enthalten ist.Awk + Paste
Das obige kann mit Paste vereinfacht werden:
Der
paste
Befehl wird verwendet, um die awk-Ausgabe mit Doppelpunkten zu durchsetzten. Dies vereinfacht das Drucken der awk-Aktion (dies ist die Standardaktion).Python
Das gleiche wie Python Two-Liner:
quelle
paste
Befehl funktioniert bei mir nur, wenn ich ein Trailing hinzufüge-
, um STDIN zu verwenden.-v
sonst erhalte ich eine Fehlermeldung.-v RS=: -v ORS=
. Nur verschiedene Arten vonawk
Syntax.Es hat eine ähnliche Diskussion darüber gewesen hier .
Ich gehe ein bisschen anders vor. Anstatt nur den
getconf
Pfad zu akzeptieren, der aus den verschiedenen zu installierenden Initialisierungsdateien festgelegt wurde , identifiziere ich lieber den Systempfad und platziere ihn zuerst, füge dann meine bevorzugte Pfadreihenfolge hinzuawk
und entferne dann alle Duplikate. Dies kann die Befehlsausführung wirklich beschleunigen oder nicht (und ist theoretisch sicherer), führt aber zu warmen Unschärfen.quelle
:
zumPATH
(dh einen leeren Zeichenfolgeneintrag) hinzufügen , da dann das aktuelle Arbeitsverzeichnis Teil Ihres Arbeitsverzeichnisses istPATH
.Solange wir nicht-awk Oneliners hinzufügen:
(Könnte so einfach sein,
PATH=$(zsh -fc 'typeset -U path; echo $PATH')
aber zsh liest immer mindestens einezshenv
Konfigurationsdatei, die geändert werden kannPATH
.)Es verwendet zwei nette zsh-Funktionen:
typeset -T
)typeset -U
).quelle
Dies verwendet Perl und hat mehrere Vorteile:
/usr/bin:/sbin:/usr/bin
wird dazu führen/usr/bin:/sbin
)quelle
Auch
sed
(hier mit GNU-sed
Syntax) kann die Arbeit erledigen:Dieser funktioniert nur dann gut, wenn der erste Pfad
.
dem Beispiel von dogbane entspricht.Im Allgemeinen müssen Sie noch einen weiteren
s
Befehl hinzufügen :Es funktioniert auch bei solchen Konstruktionen:
quelle
Wie andere gezeigt haben, ist es in einer Zeile möglich, awk, sed, perl, zsh oder bash zu verwenden. Dies hängt von Ihrer Toleranz für lange Zeilen und Ihrer Lesbarkeit ab. Hier ist eine Bash-Funktion, die
Bash-Funktion
Verwendungszweck
So entfernen Sie Dups aus PATH
quelle
Das ist meine Version:
Verwendungszweck:
path_no_dup "$PATH"
Beispielausgabe:
quelle
Aktuelle Bash-Versionen (> = 4) auch von assoziativen Arrays, dh Sie können auch einen Bash-Einzeiler dafür verwenden:
wo:
IFS
Ändert das Eingabefeld-Trennzeichen in:
declare -A
deklariert ein assoziatives Array${a[$i]+_}
ist eine Parametererweiterung Bedeutung:_
wird nur dann ersetzt, wenna[$i]
gesetzt. Dies ähnelt dem,${parameter:+word}
der auch auf not-null testet. In der folgenden Auswertung der Bedingung wird der Ausdruck_
(dh eine einzelne Zeichenfolge) mit true ausgewertet (dies entspricht-n _
), während ein leerer Ausdruck mit false ausgewertet wird.quelle
${a[$i]+_}
Bearbeiten Sie Ihre Antwort und fügen Sie einen Aufzählungspunkt hinzu. Der Rest ist vollkommen verständlich, aber du hast mich dort verloren. Danke.Erklärung des awk-Codes:
Dieser Einzeiler ist nicht nur kurz, sondern auch schnell: awk verwendet eine Verkettungstabelle, um eine amortisierte O (1) -Leistung zu erzielen.
basierend auf Entfernen doppelter $ PATH-Einträge
quelle
if ( !x[$i]++ )
. Vielen Dank.Verwenden Sie
awk
diese Option , um den Pfad weiter zu teilen, die:
Schleife über jedes Feld zu führen und es in einem Array zu speichern. Wenn Sie auf ein Feld stoßen, das sich bereits im Array befindet, bedeutet dies, dass Sie es bereits gesehen haben, drucken Sie es also nicht aus.Hier ist ein Beispiel:
(Aktualisiert, um das Ende zu entfernen
:
.)quelle
Eine Lösung - nicht so elegant wie jene, die die * RS-Variablen ändern, aber vielleicht einigermaßen klar:
Das gesamte Programm arbeitet in den Bausteinen BEGIN und END . Es zieht Ihre PATH-Variable aus der Umgebung und teilt sie in Einheiten auf. Es durchläuft dann das resultierende Array p (das in der Reihenfolge von erstellt wird
split()
). Das Array e ist ein assoziatives Array, mit dem bestimmt wird, ob wir das aktuelle Pfadelement (z. B. / usr / local / bin ) bereits gesehen haben oder nicht. Wenn nicht, wird es an np angehängt , wobei ein Doppelpunkt angefügt wird np wenn es bereits text in np gibt . Der END- Block gibt einfach np wieder . Dies könnte noch weiter vereinfacht werden, indem die-F:
flag, eliminiert das dritte Argument vonsplit()
(da es standardmäßig FS ist ) und ändert sichnp = np ":"
zunp = np FS
, was uns ergibt:Naiv, ich habe geglaubt, das
for(element in array)
würde Ordnung bewahren, aber das tut es nicht, also funktioniert meine ursprüngliche Lösung nicht, da sich die Leute aufregen würden, wenn jemand plötzlich die Reihenfolge ihrer Dinge durcheinanderwirbelt$PATH
:quelle
Nur das erste Vorkommen wird beibehalten und die relative Reihenfolge wird beibehalten.
quelle
Ich würde es nur mit grundlegenden Werkzeugen wie tr, sort und uniq machen:
Wenn sich nichts Besonderes oder Verrücktes auf Ihrem Weg befindet, sollte es funktionieren
quelle
sort -u
anstelle von verwendensort | uniq
.