Vra

Is daar 'n idiomatiese manier om elemente van PATH-agtige dop veranderlikes te verwyder?

Dit is wat ek wil neem

PATH=/home/joe/bin:/usr/local/bin:/usr/bin:/bin:/path/to/app/bin:.

en verwyder of vervang die /path/to/app/bin sonder om die res van die veranderlike te klop.Ekstra punte om my toe te laat sit nuwe elemente in arbitrêre posisies.Die teiken sal herkenbaar wees deur 'n goed gedefinieerde string, en kan op enige punt in die lys voorkom.

Ek weet ek het dit al gesien gedoen, en kan seker op my eie iets aanmekaar slaan, maar ek soek 'n mooi benadering.Draagbaarheid en standaardisering 'n pluspunt.

Ek gebruik bash, maar voorbeelde is ook welkom in jou gunsteling dop.


Die konteks hier is een van die behoefte om gerieflik te wissel tussen veelvuldige weergawes (een vir die doen van analise, 'n ander om aan die raamwerk te werk) van 'n groot wetenskaplike analise-pakket wat 'n paar dosyn uitvoerbares produseer, data in die lêerstelsel gestoor het en omgewingsveranderlike gebruik om te help om al hierdie goed te vind.Ek wil graag 'n skrip skryf wat 'n weergawe kies, en moet die $PATH elemente wat verband hou met die tans aktiewe weergawe en vervang dit met dieselfde elemente wat verband hou met die nuwe weergawe.


Dit hou verband met die probleem om herhaling te voorkom $PATH elemente wanneer aanmeldskrifte en dies meer herhaal word.


Was dit nuttig?

Oplossing

Die aanspreek van die voorgestelde oplossing van dmckee:

  1. Terwyl sommige weergawes van Bash koppeltekens mag toelaat in funksie name, ander (MacOS X) te doen nie.
  2. Ek sien nie 'n behoefte aan terugkeer onmiddellik gebruik voor die einde van die funksie.
  3. Ek kan nie sien die behoefte vir al die semi-dubbele punte.
  4. Ek kan nie sien waarom jy weg-element-vir-patroon uitvoer van 'n waarde. Dink aan export as gelykstaande aan die opstel van (of selfs die skep van) 'n globale veranderlike - iets om te vermy waar moontlik
  5. .
  6. Ek is nie seker wat jy 'replace-path PATH $PATH /usr' te doen verwag, maar dit nie doen wat ek sou verwag.

Oorweeg 'n pad waarde wat begin met:

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

Die resultaat Ek het (uit 'replace-path PATH $PATH /usr') is:

.
/Users/jleffler/bin
/local/postgresql/bin
/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/local/bin
/bin
/bin
/sw/bin
/sbin
/sbin

Ek sou verwag dat my oorspronklike pad terug te kry sedert / usr nie as 'n (volledige) element pad verskyn, net as deel van 'n element pad.

Dit kan in replace-path vasgestel deur die wysiging van een van die sed opdragte:

export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
               tr "\n" ":" | sed "s|::|:|g")

Ek gebruik ': "in plaas van' | ' om afsonderlike dele van die plaasvervanger sedert '|' kon (in teorie) verskyn in 'n komponent pad, terwyl per definisie van PATH, 'n kolon kan nie. Ek sien dat die tweede sed die huidige gids uit die middel van 'n pad kan uitskakel. Dit is, 'n wettige (alhoewel perverse) waarde van PATH kan wees:

PATH=/bin::/usr/local/bin

Na verwerking, sou die huidige gids nie meer op die pad.

'n soortgelyke verandering in die wedstryd anker is gepas in path-element-by-pattern:

export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")

Ek let in die verbygaan dat grep -m 1 is nie standaard (dit is 'n GNU uitbreiding, ook beskikbaar op MacOS X). En, inderdaad, the-n opsie vir echo is ook nie-standaard; sou jy beter daaraan toe wees die sleep kolon wat bygevoeg uit hoofde van die omskakeling van die newline van eggo in 'n kolon eenvoudig die verwydering van. Sedert pad-element-vir-patroon net een keer gebruik word, het ongewenste newe-effekte (dit clobbers enige voorafbestaande uitgevoer veranderlike genoem $removestr), dit kan sinvol vervang deur sy liggaam. Dit, tesame met meer liberale gebruik van aanhalings om probleme met spasies of ongewenste lêer naam uitbreiding te vermy, lei tot:

# 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"
}

Ek het 'n Perl script genoem echopath wat ek nuttig wanneer debugging probleme met PATH-agtige veranderlikes:

#!/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";
    }
}

Wanneer ek hardloop die gewysigde oplossing op die toets kode hieronder:

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 produksie is:

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

Dit lyk korrek om my - ten minste, vir my definisie van wat die probleem is

.

Ek daarop dat echopath LD_LIBRARY_PATH evalueer $LD_LIBRARY_PATH. Dit sal lekker wees as jou funksies in staat was om dit te doen, sodat die gebruiker kan tik:

replace_path PATH /usr/bin /work/bin

Dit kan gedoen word deur die gebruik van:

list=$(eval echo '$'$path)

Dit lei tot hierdie hersiening van die kode:

# 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"
}

Die volgende hersiene toets werk nou ook:

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

Dit produseer dieselfde uitset as tevore.

Ander wenke

herverzending my antwoord op Wat is die mees elegante manier om 'n pad uit die $ PATH veranderlike in bash verwyder :

#!/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[*]}"

of die one-liner:

PATH=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");

Vir 'n element wat jy kan sed gebruik te skrap:

#!/bin/bash
NEW_PATH=$(echo -n $PATH | tr ":" "\n" | sed "/foo/d" | tr "\n" ":")
export PATH=$NEW_PATH

sal die paaie wat "cat" uit die pad bevat verwyder.

Jy kan ook sed gebruik om 'n nuwe lyn in te voeg voor of na 'n gegewe lyn.

Edit: jy kan duplikate te verwyder deur spuit deur sorteer en UNIQ:

echo -n $PATH | tr ":" "\n" | sort | uniq -c | sed -n "/ 1 / s/.*1 \(.*\)/\1/p" | sed "/foo/d" | tr "\n" ":"

Daar is 'n paar relevante programme in die antwoorde op "Hoe om te verhoed dat die padveranderlike in csh gedupliseer word".Hulle konsentreer meer daarop om te verseker dat daar geen herhaalde elemente is nie, maar die skrif wat ek verskaf kan gebruik word as:

export PATH=$(clnpath $head_dirs:$PATH:$tail_dirs $remove_dirs)

Gestel jy het een of meer gidse in $head_dirs en een of meer gidse in $tail_dirs en een of meer gidse in $remove_dirs, dan gebruik dit die dop om die kop-, stroom- en stertdele in 'n massiewe waarde saam te voeg, en verwyder dan elkeen van die dopgehou wat in $remove_dirs van die resultaat gelys is (nie 'n fout as hulle nie bestaan ​​nie), asook die uitskakeling van tweede en daaropvolgende voorkomste van enige gids in die pad.

Dit spreek nie die plaas van padkomponente in 'n spesifieke posisie aan nie (behalwe aan die begin of einde, en dié slegs indirek).Notasioneel is dit morsig om te spesifiseer waar jy die nuwe element wil byvoeg, of watter element jy wil vervang.

Net 'n nota dat bash self kan doen soek en te vervang. Dit kan al die normale "een of all", gevalle doen [in] sensitiewe opsies wat jy sou verwag.

Van die man bladsy:

$ {parameter / patroon / string}

Die patroon is uitgebrei om 'n patroon te produseer net so in padnaam uitbreiding. Parameter is uitgebrei en die langste wedstryd van patroon teen die waarde daarvan is vervang met 'n tou. As Ipattern begin met /, is almal wedstryde van patroon vervang met 'n tou. Gewoonlik net die eerste wedstryd vervang. As patroon begin met #, moet dit ooreenstem aan die begin van die uitgebreide waarde van parameter. As patroon begin met%, moet dit ooreenstem aan die einde van die uitgebreide waarde van parameter. As string is nul, is wedstryde van patroon verwyder en die / volgende patroon kan weggelaat word. As parameter is @ of *, Is die vervanging operasie toegepas op elke posisionele parameter op sy beurt, en die uitbreiding is die gevolglike lys. As parameter is 'n verskeidenheid veranderlike subscripted met @ of *, Is die vervanging operasie toegepas op elke lid van die skikking op sy beurt, en die uitbreiding is die gevolglike lys.

Jy kan ook veld verdeel deur die oprigting van $ IFS (insette veld separator) om die gewenste delimiter.

OK, dankie aan almal reageer. Ek het 'n vasgevang weergawe van antwoord Florin se voorberei. Die eerste pass lyk soos volg:

# 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;
}

moet nog fout wild in al die funksies, en ek moet seker stok in 'n herhaalde oplossing pad terwyl ek by dit.

Jy gebruik dit deur 'n . /include/path/path_tools.bash in die werk script en 'n beroep op die die replace-path* funksies.


Ek is nog steeds oop vir nuwe en / of beter antwoorde.

Dit is maklik om awk te gebruik.

Vervang

{
  for(i=1;i<=NF;i++) 
      if($i == REM) 
          if(REP)
              print REP; 
          else
              continue;
      else 
          print $i; 
}

Begin dit gebruik

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

Voeg by

Voeg by die gegewe posisie in.By verstek word dit aan die einde bygevoeg.

{ 
    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
}

Begin dit gebruik

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

Verwyder duplikate

Hierdie een hou die eerste gebeurtenisse.

{ 
    for(i = 1; i <= NF; i++) {
        if(!used[$i]) {
            print $i
            used[$i] = 1
        }
    }
}

Begin dit so:

echo $PATH | awk -F: -f rem_dup.awk | paste -sd:

Bevestig of alle elemente bestaan

Die volgende sal 'n foutboodskap druk vir alle inskrywings wat nie in die lêerstelsel bestaan ​​nie, en 'n waarde wat nie nul is nie.

echo -n $PATH | xargs -d: stat -c %n

Om eenvoudig te kyk of alle elemente paaie is en 'n terugkeerkode te kry, kan jy ook gebruik test:

echo -n $PATH | xargs -d: -n1 test -d

veronderstel

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

As jy wil verwyder /lib/jvm/java-1.6.0/bin/ doen soos hieronder

export PATH=$(echo $PATH | sed  's/\/lib\/jvm\/java-1.6.0\/bin\/://g')

sed sal insette te neem van echo $PATH en vervang /lib/jvm/java-1.6.0/bin/: met leë

in hierdie manier kan jy verwyder

  • Orde van PATH is nie versteur
  • Hanteer hoek gevalle soos leë pad, ruimte in pad grasieus
  • Gedeeltelike wedstryd van dir gee nie vals positiewes
  • Behandel pad na die kop en stert van PATH in behoorlike maniere. Geen : vullis en so
  • .

Sê jy     / Cat: / sommige / pad: / sommige / pad / map1: / sommige / pad / map2: / bar en wat jy wil om te vervang     / 'N / pad Dan is dit korrek vervang "/ sommige / pad", maar blare "/ sommige / pad / map1" of "/ sommige / pad / map2", as wat jy sou verwag.

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  
}  

Verwante post Wat is die mees elegante manier om 'n pad uit die $ PATH veranderlike in bash verwyder?

Ek verkies die gebruik van Ruby om die hou van awk / sed / cat deesdae, so hier is my benadering te hanteer dupes,

# add it to the path
PATH=~/bin/:$PATH:~/bin
export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')

skep 'n funksie vir hergebruik,

mungepath() {
   export PATH=$(ruby -e 'puts ENV["PATH"].split(/:/).uniq.join(":")')
}

Hash, skikkings en snare in 'n robyn een sak:)

Die eerste ding om te pop in my kop om net deel van 'n string te verander is 'n sed vervanging.

voorbeeld: As eggo $ PATH => "/ usr / pkg / bin: / usr / bin: / bin: / usr / pkg / speletjies: / usr / pkg / X11R6 / bin" dan na "/ usr / bin" verander na "/ usr / local / bin" gedoen kan word soos volg:

## produseer standaard uitset lêer

## die "=" karakter gebruik word in plaas van slash ( "/") want dit morsig wees,  # Alternatiewe vermelding karakter moet onwaarskynlik in PATH wees

## die pad Separater karakter ":" is beide verwyder en hier weer bygevoeg,  # Dalk 'n ekstra kolon na die laaste pad

eggo $ PATH | sed '= / usr / bin: = / usr / local / bin: ='

Hierdie oplossing vervang 'n hele pad-element so dalk oorbodig wees as nuwe element is soortgelyk.

As die nuwe PATH'-s is nie dinamiese maar altyd binne 'n paar konstante stel jy kan red die in 'n veranderlike en toewys as wat nodig is:

PATH = $ TEMP_PATH_1;  # Beveel ...; \ N PATH = TEMP_PATH_2 $;  # Opdragte ens ...

dalk nie wat jy dink. sommige van die relevante opdragte op bash / unix sou wees:

pushd popd cd LS # miskien l 1 bis vir enkele kolom; vind grep wat # lêer kon bevestig word waar jy dink dit vandaan kom; env tipe

.. en alles wat en meer hê 'n paar betrekking het op pad of dopgehou in die algemeen. Die teks verandering deel kan enige aantal van maniere gedoen word!

Wat oplossing gekies sou hê 4 dele:

1) haal die pad, want dit is 2) ontsyfer die pad na die gedeelte hoef veranderinge vind 3) medebepalend wat veranderinge nodig / integrasie dié veranderinge 4) bekragtiging / finale integrasie / opstel van die veranderlike

In lyn met antwoord dj_segfault se , ek doen dit in skrifte wat / plaas jou omgewing veranderlikes wat meer as een keer kan word uitgevoer voeg :

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

Die gebruik van hierdie selfde tegniek om te verwyder, te vervang of inskrywings in PATH manipuleer is triviale gegewe die lêernaam-uitbreiding-agtige patroon ooreenstem en patroon-lys ondersteuning van dop parameter uitbreiding.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top