Wie kann ich $ PATH Elemente in Shell-Skripten manipulieren?
-
07-07-2019 - |
Frage
Gibt es eine idiomatische Weise Elemente von PATH-ähnlichen Shell-Variablen zu entfernen?
Das ist ich nehmen will
PATH=/home/joe/bin:/usr/local/bin:/usr/bin:/bin:/path/to/app/bin:.
und Entfernen oder ersetzen die /path/to/app/bin
, ohne den Rest der Variablen clobbering. Extra-Punkte für das Erlauben mir, put neue Elemente in beliebigen Positionen. Das Ziel wird durch eine gut definierte Zeichenfolge erkennbar sein, und an einer beliebigen Stelle in der Liste auftreten können.
Ich weiß, ich habe dies getan, gesehen und kann wahrscheinlich etwas zusammen auf meinem eigenen schustern, aber ich bin auf der Suche nach einem schönen Ansatz. Portabilität und Standardisierung ein Plus.
Ich verwende bash, aber beispielsweise in Ihren Lieblingsschal sind willkommen auch.
Der Kontext hier ist eine von benötigen bequem zwischen mehreren Versionen wechseln (eine für die Analyse zu tun, ein anderer auf dem Rahmen für die Arbeit) einer großen wissenschaftlichen Analyse-Paket, das ein paar Dutzend Executables erzeugt, Daten rund um das Dateisystem gebunkert hat, und nutzt Umgebungsvariable all dieses Zeug zu finden. Ich möchte ein Skript schreiben, die eine Version auswählt, und müssen in der Lage sein, die $PATH
Elemente auf die derzeit aktive Version beziehen, zu entfernen und ersetzen sie durch die gleichen Elemente auf die neue Version beziehen.
Dies ist auf das Problem verwandte Elemente zu verhindern, wiederholte $PATH
wenn Anmeldeskripts erneut ausgeführt und dergleichen.
- Zurück ähnliche Frage: Wie aus Duplizieren Pfadvariablen halten in csh
- Im Anschluss ähnliche Frage: Was ist die eleganteste Art und Weise einen Weg von dem $ PATH-Variable in bash? zu entfernen
Lösung
, die vorgeschlagene Lösung von Dmckee Adressierung:
- Während einige Versionen von Bash können Bindestriche in Funktionsnamen erlauben, andere (MacOS X) nicht.
- Ich habe keinen Bedarf sieht Rückkehr zu verwenden unmittelbar vor dem Ende der Funktion.
- Ich sehe nicht die Notwendigkeit für alle Semikolons.
- Ich verstehe nicht, warum Sie Pfad-Element-by-Muster haben einen Wert exportieren. Denken Sie an
export
als gleichwertig Einstellung (oder sogar die Schaffung) eine globale Variable - etwas werden, wann immer möglich zu vermeiden .
- Ich bin mir nicht sicher, was du ‚
replace-path PATH $PATH /usr
‘ zu tun erwarten, aber es tut nicht, was ich erwarten würde.
einen PATH-Wert Betrachten Sie das enthalten beginnt:
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
Das Ergebnis, das ich bekam (von 'replace-path PATH $PATH /usr
') ist:
.
/Users/jleffler/bin
/local/postgresql/bin
/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/local/bin
/bin
/bin
/sw/bin
/sbin
/sbin
ich hätte erwartet, dass meine ursprünglichen Pfad zurück, da / usr nicht als (komplett) Pfadelement angezeigt wird, nur als Teil eines Pfadelement.
Dies kann durch Modifikation eines der replace-path
Befehle in sed
festgelegt werden:
export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
tr "\n" ":" | sed "s|::|:|g")
Ich habe ':' statt '|' In den einzelnen Teilen des Ersatzes seit ‚|‘ (In der Theorie) könnte in einer Pfadkomponente erscheinen, während durch Definition von PATH, kann ein Doppelpunkt nicht. Ich beobachte, dass die zweite sed
das aktuelle Verzeichnis aus der Mitte eines PATH beseitigen könnte. Das heißt, ein legitimer (wenn auch perverse) Wert von PATH könnte sein:
PATH=/bin::/usr/local/bin
Nach der Verarbeitung würde das aktuelle Verzeichnis nicht mehr auf dem Pfad sein.
Eine ähnliche Veränderung, das Spiel zu verankern ist angemessen in path-element-by-pattern
:
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")
Ich nehme zur Kenntnis, dass grep -m 1
nebenbei ist nicht Standard (es ist eine GNU-Erweiterung, auch auf MacOS X). Und in der Tat ist the-n
Option für echo
auch Nicht-Standard; Sie wäre besser dran, einfach sein, den hinteren Doppelpunkt zu löschen, die aufgrund der Umwandlung der Newline von Echos in einen Doppelpunkt hinzugefügt wird. Da Pfad-Element-für-Muster nur einmal verwendet wird, hat unerwünschte Nebenwirkungen (es irgendeine Variable namens $removestr
vorbestehenden exportiert clobbers), kann er durch seinen Körper sinnvoll ersetzt werden. Dies, zusammen mit liberaler Verwendung von Zitaten Probleme mit Leerzeichen oder unerwünschten Dateinamen Expansion zu vermeiden, führt zu:
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH $PATH /exact/path/to/remove
# replace_path_pattern PATH $PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH $PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH $PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 the precise string to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$2
remove=$3
replace=$4 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 a grep pattern identifying the element to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$2
removepat=$3
replacestr=$4 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$list" "$removestr" "$replacestr"
}
Ich habe ein Perl-Skript namens echopath
, die ich nützlich, wenn Probleme mit PATH-Variablen wie das Debuggen:
#!/usr/bin/perl -w
#
# "@(#)$Id: echopath.pl,v 1.7 1998/09/15 03:16:36 jleffler Exp $"
#
# Print the components of a PATH variable one per line.
# If there are no colons in the arguments, assume that they are
# the names of environment variables.
@ARGV = $ENV{PATH} unless @ARGV;
foreach $arg (@ARGV)
{
$var = $arg;
$var = $ENV{$arg} if $arg =~ /^[A-Za-z_][A-Za-z_0-9]*$/;
$var = $arg unless $var;
@lst = split /:/, $var;
foreach $val (@lst)
{
print "$val\n";
}
}
Wenn ich die modifizierte Lösung auf dem Testcode laufen unter:
echo
xpath=$PATH
replace_path xpath $xpath /usr
echopath $xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath /usr/bin /work/bin
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath "/usr/.*/bin" /work/bin
echopath xpath
Die Ausgabe lautet:
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/work/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/work/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
Das sieht richtig zu mir - zumindest für meine Definition dessen, was das Problem ist,
. Ich nehme zur Kenntnis, dass echopath LD_LIBRARY_PATH
wertet $LD_LIBRARY_PATH
. Es wäre schön, wenn Ihre Funktionen der Lage sind, das zu tun, so könnte der Benutzer eingeben:
replace_path PATH /usr/bin /work/bin
Das kann durch die Verwendung durchgeführt werden:
list=$(eval echo '$'$path)
Dies führt zu dieser Revision des Codes:
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH /exact/path/to/remove
# replace_path_pattern PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 the precise string to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$(eval echo '$'$path)
remove=$2
replace=$3 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a grep pattern identifying the element to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$(eval echo '$'$path)
removepat=$2
replacestr=$3 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$removestr" "$replacestr"
}
Der folgende überarbeitete Test funktioniert nun auch:
echo
xpath=$PATH
replace_path xpath /usr
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath /usr/bin /work/bin
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath "/usr/.*/bin" /work/bin
echopath xpath
Es erzeugt die gleiche Ausgabe wie zuvor.
Andere Tipps
Umbuchen meiner Antwort auf Was ist die eleganteste Art und Weise einen Weg von dem $ PATH-Variable in bash zu entfernen:?
#!/bin/bash
IFS=:
# convert it to an array
t=($PATH)
unset IFS
# perform any array operations to remove elements from the array
t=(${t[@]%%*usr*})
IFS=:
# output the new array
echo "${t[*]}"
oder die Einzeiler:
PATH=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");
Für ein Element löschen Sie sed verwenden können:
#!/bin/bash
NEW_PATH=$(echo -n $PATH | tr ":" "\n" | sed "/foo/d" | tr "\n" ":")
export PATH=$NEW_PATH
werden die Pfade löschen, die „foo“ aus dem Pfad enthalten.
Sie können auch verwenden, sed vor oder nach einer bestimmten Zeile eine neue Zeile einzufügen.
Edit: Sie können Duplikate entfernen von Rohrleitungen durch Art und uniq:
echo -n $PATH | tr ":" "\n" | sort | uniq -c | sed -n "/ 1 / s/.*1 \(.*\)/\1/p" | sed "/foo/d" | tr "\n" ":"
OK, Dank an alle Responder. Ich habe eine gekapselte Version von Florins Antwort vorbereitet. Der erste Durchlauf sieht wie folgt aus:
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace-path PATH $PATH /exact/path/to/remove
# replace-path-pattern PATH $PATH <grep pattern for target path>
#
# To replace a path:
# replace-path PATH $PATH /exact/path/to/remove /replacement/path
# replace-path-pattern PATH $PATH <target pattern> /replacement/path
#
###############################################################################
# Finds the _first_ list element matching $2
#
# $1 name of a shell variable to be set
# $2 name of a variable with a path-like structure
# $3 a grep pattern to match the desired element of $1
function path-element-by-pattern (){
target=$1;
list=$2;
pat=$3;
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 $pat);
return
}
# Removes or replaces an element of $1
#
# $1 name of the shell variable to set (i.e. PATH)
# $2 a ":" delimited list to work from (i.e. $PATH)
# $2 the precise string to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace-path () {
path=$1;
list=$2;
removestr=$3;
replacestr=$4; # Allowed to be ""
export $path=$(echo -n $list | tr ":" "\n" | sed "s|$removestr|$replacestr|" | tr "\n" ":" | sed "s|::|:|g");
unset removestr
return
}
# Removes or replaces an element of $1
#
# $1 name of the shell variable to set (i.e. PATH)
# $2 a ":" delimited list to work from (i.e. $PATH)
# $2 a grep pattern identifying the element to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace-path-pattern () {
path=$1;
list=$2;
removepat=$3;
replacestr=$4; # Allowed to be ""
path-element-by-pattern removestr $list $removepat;
replace-path $path $list $removestr $replacestr;
}
muss noch die Fehlerbehebung in allen Funktionen, und ich soll wohl in einer wiederholten Pfad Lösung bleiben, während ich es bin.
Sie verwenden es, durch eine . /include/path/path_tools.bash
im Arbeits Skript zu tun und auf der die replace-path*
Funktionen aufrufen.
Ich bin immer noch offen für neue und / oder bessere Antworten.
Das ist einfach awk verwendet wird.
Ersetzen
{
for(i=1;i<=NF;i++)
if($i == REM)
if(REP)
print REP;
else
continue;
else
print $i;
}
Starten Sie es mit
function path_repl {
echo $PATH | awk -F: -f rem.awk REM="$1" REP="$2" | paste -sd:
}
$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_repl /bin /baz
/baz:/usr/bin:/home/js/usr/bin
$ path_repl /bin
/usr/bin:/home/js/usr/bin
Anfügen
Einsätze an der angegebenen Position. Standardmäßig hängt es am Ende.
{
if(IDX < 1) IDX = NF + IDX + 1
for(i = 1; i <= NF; i++) {
if(IDX == i)
print REP
print $i
}
if(IDX == NF + 1)
print REP
}
Starten Sie es mit
function path_app {
echo $PATH | awk -F: -f app.awk REP="$1" IDX="$2" | paste -sd:
}
$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_app /baz 0
/bin:/usr/bin:/home/js/usr/bin:/baz
$ path_app /baz -1
/bin:/usr/bin:/baz:/home/js/usr/bin
$ path_app /baz 1
/baz:/bin:/usr/bin:/home/js/usr/bin
Duplikate entfernen
Dies hält die ersten Vorkommen.
{
for(i = 1; i <= NF; i++) {
if(!used[$i]) {
print $i
used[$i] = 1
}
}
}
Starten Sie es wie folgt aus:
echo $PATH | awk -F: -f rem_dup.awk | paste -sd:
Validieren, ob alle Elemente vorhanden
Im Folgenden wird eine Fehlermeldung für alle Einträge drucken, die im Dateisystem vorhanden sind, werden nicht und geben einen Wert ungleich Null.
echo -n $PATH | xargs -d: stat -c %n
Um einfach zu überprüfen, ob alle Elemente sind Pfade und einen Return-Code erhalten, können Sie auch test
verwenden:
echo -n $PATH | xargs -d: -n1 test -d
annehmen
echo $PATH
/usr/lib/jvm/java-1.6.0/bin:lib/jvm/java-1.6.0/bin/:/lib/jvm/java-1.6.0/bin/:/usr/lib/qt-3.3/bin:/usr/lib/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/tvnadeesh/bin
Wenn Sie möchten, wie, wie unten /lib/jvm/java-1.6.0/bin/ zu tun entfernen
export PATH=$(echo $PATH | sed 's/\/lib\/jvm\/java-1.6.0\/bin\/://g')
sed
nehmen Eingabe von echo $PATH
und ersetzen /lib/jvm/java-1.6.0/bin/: mit leeren
Auf diese Weise können Sie entfernen
- Bestellen von PATH ist nicht distrubed
- Griffe Eckfällen wie leerer Pfad, Raum in Pfad anmutig
- Partial Match von dir nicht geben Fehlalarme
- Behandelt Weg an Kopf und Schwanz von PATH in der richtigen Art und Weise. Nein : Abfall und so .
Angenommen, Sie haben / Foo: / some / path: / some / path / dir1: / some / path / dir2: / bar und Sie ersetzen möchten / Some / path Dann ersetzt es richtig „/ some / path“ aber Blätter "/ some / path / dir1" oder "/ some / path / dir2", wie das, was man erwarten würde.
function __path_add(){
if [ -d "$1" ] ; then
local D=":${PATH}:";
[ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
fi
}
function __path_remove(){
local D=":${PATH}:";
[ "${D/:$1:/:}" != "$D" ] && PATH="${D/:$1:/:}";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
}
# Just for the shake of completeness
function __path_replace(){
if [ -d "$2" ] ; then
local D=":${PATH}:";
if [ "${D/:$1:/:}" != "$D" ] ; then
PATH="${D/:$1:/:$2:}";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
fi
fi
}
In Verbindung stehende Pfosten Was ist die eleganteste Art und Weise einen Weg von dem $ PATH-Variable in Bash?
zu entfernenIch ziehe mit Rubin, die gerne von awk / sed / foo in diesen Tagen, so ist hier mein Ansatz mit Betrogenen beschäftigen,
# add it to the path
PATH=~/bin/:$PATH:~/bin
export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')
Erstellen Sie eine Funktion für die Wiederverwendung,
mungepath() {
export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')
}
Hash, Arrays und Strings in einem Rubin Motto:)
Das erste, was ich in dem Kopf Pop nur einen Teil einer Zeichenfolge zu ändern, ist eine Sed-Substitution.
Beispiel: wenn echo $ PATH => "/ usr / pkg / bin: / usr / bin: / bin: / usr / pkg / Spiele: / usr / pkg / X11R6 / bin" dann ändern "/ usr / bin" in "/ usr / local / bin", wie dies geschehen könnte:
## erzeugt Standardausgabedatei
## das Zeichen „=“ wird anstelle von Schrägstrich verwendet ( „/“), da das chaotisch wäre, # Alternative zitierte Zeichen sollte in PATH
unwahrscheinlich## den Weg separater Zeichen „:“ beide entfernt und erneut hinzugefügt hier, # Könnte einen zusätzlichen Doppelpunkt nach dem letzten Weg will
echo $ PATH | sed '= / usr / bin: = / usr / local / bin: ='
ersetzt Diese Lösung ein ganzes Pfad-Element so überflüssig sein könnte, wenn neues Element ähnlich ist.
Wenn die neue PATH'-s nicht dynamisch sind, aber immer innerhalb eines konstanten Satzes könnten Sie speichern diejenigen in einer Variablen und weist je nach Bedarf:
PATH = $ TEMP_PATH_1; # Befehle ...; \ n PATH = $ TEMP_PATH_2; # Befehle etc ...;
Könnte nicht das, was Sie dachten. einige der entsprechenden Befehle auf bash / Unix wäre:
pushd popd CD ls # vielleicht l -1 A für einzelne Säule; finden grep worin # könnte diese Datei bestätigen, wo Sie denken, es kam; env Typ
.. und all das und mehr haben einige auf PATH oder Verzeichnisse im Allgemeinen tragen. Der Text Änderungs Teil eine beliebige Anzahl von Arten erfolgen kann!
Welche Lösung auch immer gewählt würde 4 Teile:
1) holen den Pfad wie er ist 2) dekodieren den Pfad der Teil, um Änderungen zu finden 3) Bestimmen, welche Änderungen nötig sind, um / Integration jene Änderungen 4) Validierung / Abschluss Integration / Einstellung der Variablen
Im Einklang mit dj_segfault Antwort , Ich tue dies in Skripten, die / prepend Umgebungsvariablen anhängen, die mehrfach ausgeführt werden können :
ld_library_path=${ORACLE_HOME}/lib
LD_LIBRARY_PATH=${LD_LIBRARY_PATH//${ld_library_path}?(:)/}
export LD_LIBRARY_PATH=${ld_library_path}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
Unter Verwendung dieser gleichen Technik zu entfernen, ersetzen oder Einträge in PATH zu manipulieren trivial gegeben ist der Dateiname-Erweiterungsartige Musteranpassung und Musterliste Unterstützung der Schalenparameter Expansion.