बाश में $ PATH चर से एक रास्ता निकालने का सबसे सुरुचिपूर्ण तरीका क्या है?


114

या अधिक आम तौर पर, मैं एक बैश वातावरण चर में एक औपनिवेशिक-पृथक सूची से आइटम कैसे निकालूं?

मुझे लगा कि मैंने बैश चर विस्तार के अधिक उन्नत रूपों का उपयोग करते हुए इस साल पहले एक सरल तरीका देखा था, लेकिन अगर मैंने इसका ट्रैक खो दिया है। Google की त्वरित खोज ने आश्चर्यजनक रूप से कुछ प्रासंगिक परिणामों को बदल दिया और कोई नहीं जिसे मैं "सरल" या "सुरुचिपूर्ण" कहूंगा। उदाहरण के लिए, दो तरीकों का उपयोग करके, sed और awk क्रमशः:

PATH=$(echo $PATH | sed -e 's;:\?/home/user/bin;;' -e 's;/home/user/bin:\?;;')
PATH=!(awk -F: '{for(i=1;i<=NF;i++){if(!($i in a)){a[$i];printf s$i;s=":"}}}'<<<$PATH)

क्या कुछ भी सीधा नहीं है? क्या बाश में विभाजित () फ़ंक्शन के अनुरूप कुछ है?

अद्यतन:
ऐसा लगता है कि मुझे अपने जानबूझकर अस्पष्ट प्रश्न के लिए माफी माँगने की आवश्यकता है; मैं अच्छी चर्चा को भड़काने की तुलना में विशिष्ट उपयोग के मामले को हल करने में कम रुचि रखता था। सौभाग्य से, मुझे मिल गया!

यहाँ कुछ बहुत ही चतुर तकनीकें हैं। अंत में, मैंने अपने टूलबॉक्स में निम्नलिखित तीन फ़ंक्शन जोड़े हैं। जादू path_remove में होता है, जो काफी हद तक मार्टिन के awkRS वेरिएबल के चतुर उपयोग पर आधारित है ।

path_append ()  { path_remove $1; export PATH="$PATH:$1"; }
path_prepend () { path_remove $1; export PATH="$1:$PATH"; }
path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; }

वहाँ केवल असली cruft वहाँ का उपयोग sedकर्षण बृहदान्त्र को हटाने के लिए है। यह देखते हुए कि मार्टिन के बाकी समाधान कितने सीधे हैं, हालांकि, मैं इसके साथ रहने के लिए काफी इच्छुक हूं!


संबंधित प्रश्न: मैं शेल स्क्रिप्ट में $ PATH तत्वों का हेरफेर कैसे करूं?


किसी भी चर के लिए: WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"लेकिन आप इसे func $VAR VAR pattern(@ मार्टिन-यॉर्क और @ -rew-aylett पर आधारित)
vesperto

जवाबों:


51

जाग के साथ एक मिनट:

# Strip all paths with SDE in them.
#
export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}'`

संपादित करें: यह नीचे टिप्पणी की प्रतिक्रिया:

$ export a="/a/b/c/d/e:/a/b/c/d/g/k/i:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i"
$ echo ${a}
/a/b/c/d/e:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i

## Remove multiple (any directory with a: all of them)
$ echo ${a} | awk -v RS=: -v ORS=: '/a/ {next} {print}'
## Works fine all removed

## Remove multiple including last two: (any directory with g)
$ echo ${a} | awk -v RS=: -v ORS=: '/g/ {next} {print}'
/a/b/c/d/e:/a/b/c/d/f:
## Works fine: Again!

सुरक्षा समस्या के जवाब में संपादित करें: (यह सवाल के लिए प्रासंगिक नहीं है)

export PATH=$(echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}' | sed 's/:*$//')

यह अंतिम प्रविष्टियों को हटाकर किसी भी अनुगामी कॉलोन को हटा देता है, जो प्रभावी रूप .से आपके पथ में जोड़ देगा ।


1
अंतिम तत्व या कई तत्वों को हटाने की कोशिश करते समय विफल रहता है: पहले मामले में, यह वर्तमान डायर (खाली स्ट्रिंग; एक संभावित सुरक्षा छेद) के रूप में जोड़ता है, दूसरे मामले में यह एक पथ तत्व के रूप में `` जोड़ता है।
फ्रेड फू

1
@larsmans: मेरे लिए ठीक काम करता है। नोट: खाली वर्तमान निर्देशिका के समान नहीं है जो "./" है
मार्टिन यॉर्क

2
एक "सदस्य" के रूप में एक खाली स्ट्रिंग PATHचर करता है , एक विशेष नियम के रूप में, सभी यूनिक्स के गोले 1979 के बाद से कम से कम V7 यूनिक्स यह अभी भी में करता है में निरूपित वर्तमान निर्देशिका bash। मैनुअल की जाँच करें या अपने लिए प्रयास करें।
फ्रेड फू

1
@ मॉर्टिन: पोसिक्स को इस व्यवहार की आवश्यकता नहीं है, लेकिन दस्तावेज़ और इसे अनुमति देता है: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
फ्रेड फू

1
इसके साथ अंतिम तत्व को हटाते समय एक और भी अधिक सूक्ष्म मुद्दा होता है: awk स्ट्रिंग के अंत में एक अशक्त बाइट जोड़ना प्रतीत होता है । इसका मतलब है कि यदि आप किसी अन्य निर्देशिका को बाद में PATH में जोड़ते हैं, तो यह वास्तव में खोजा नहीं जाएगा।
sschuberth

55

मेरा गंदा हैक:

echo ${PATH} > t1
vi t1
export PATH=$(cat t1)

18
यह एक अच्छा संकेत है जब सबसे स्पष्ट समाधान करने के लिए है कभी नहीं डी -automate प्रक्रिया। :-D
बेन ब्लैंक

"सुरुचिपूर्ण" विकल्पों की तुलना में बहुत सुरक्षित।
जोर्टस्टेक

44

चूंकि प्रतिस्थापन के साथ बड़ा मुद्दा अंत के मामले हैं, कैसे अंत के मामलों को अन्य मामलों से अलग नहीं बनाने के बारे में? यदि पथ पर पहले से ही कॉलोन शुरू और अंत में थे, तो हम बस कॉलोन के साथ लिपटे अपने वांछित स्ट्रिंग की खोज कर सकते थे। जैसा कि यह है, हम आसानी से उन कॉलोनों को जोड़ सकते हैं और उन्हें बाद में निकाल सकते हैं।

# PATH => /bin:/opt/a dir/bin:/sbin
WORK=:$PATH:
# WORK => :/bin:/opt/a dir/bin:/sbin:
REMOVE='/opt/a dir/bin'
WORK=${WORK/:$REMOVE:/:}
# WORK => :/bin:/sbin:
WORK=${WORK%:}
WORK=${WORK#:}
PATH=$WORK
# PATH => /bin:/sbin

शुद्ध बैश :)।


2
मैं कुछ अतिरिक्त फ्रॉस्टिंग के लिए इस ट्यूटोरियल अनुभाग को जोड़ूंगा
साइबर ओलिवेरा

1
मैंने इसका इस्तेमाल किया क्योंकि यह सबसे सरल समाधान की तरह दिखता था। यह सुपर फास्ट और आसान था, और आप आसानी से अंतिम पंक्ति से ठीक पहले गूंज $ वर्क के साथ अपने काम की जांच कर सकते हैं जहां आप वास्तव में पैथ चर को बदलते हैं।
फिल ग्रैन

2
बिल्कुल थोड़ा रत्न। वास्तव में जब मैं इस पोस्ट को पा रहा था तो मैं क्या करने की कोशिश कर रहा था। एंड्रयू धन्यवाद! BTW: हो सकता है कि आप ": $ PATH:" के आसपास डबल-कोट्स जोड़ना चाहें, बस अगर इसमें रिक्त स्थान होना चाहिए ("/ usr / bin" के बारे में) और अंतिम पंक्ति "$ WORK"।

2
धन्यवाद, @ पचमन-- :)। मुझे पूरा यकीन है (बस इसे आज़माया गया है) आपको असाइनमेंट के लिए रिक्त स्थान की आवश्यकता नहीं है WORKऔर PATHजैसा कि वैरिएबल विस्तार तब होता है जब लाइन को वेरिएबल असाइनमेंट और कमांड निष्पादन के लिए अनुभागों में पार्स किया जाता है। REMOVEयह उद्धृत करने की आवश्यकता हो सकती है, या आप अपने स्ट्रिंग को सीधे प्रतिस्थापन में डाल सकते हैं यदि यह एक स्थिर है।
एंड्रयू आइलेट

जब तार को संरक्षित किया जाता था, तो मैं हमेशा उलझन में रहता था, इस प्रकार मैंने हमेशा डबल-उद्धरण तार को शुरू किया। इसे स्पष्ट करने के लिए फिर से धन्यवाद। :)

26

यहाँ सबसे सरल उपाय है जिसे मैं अपना सकता हूँ:

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

उपरोक्त उदाहरण $ PATH में किसी भी तत्व को हटा देगा जिसमें "usr" शामिल है। आप उस तत्व को निकालने के लिए "* usr *" को "/ होम / यूजर / बिन" से बदल सकते हैं।

अद्यतन sschuberth के अनुसार

भले ही मुझे लगता है कि अंतरिक्ष $PATHएक भयानक विचार है, यहां एक समाधान है जो इसे संभालता है:

PATH=$(IFS=':';t=($PATH);n=${#t[*]};a=();for ((i=0;i<n;i++)); do p="${t[i]%%*usr*}"; [ "${p}" ] && a[i]="${p}"; done;echo "${a[*]}");

या

IFS=':'
t=($PATH)
n=${#t[*]}
a=()
for ((i=0;i<n;i++)); do
  p="${t[i]%%*usr*}"
  [ "${p}" ] && a[i]="${p}"
done
echo "${a[*]}"

2
एक लाइनर के रूप में: PATH = $ (IFS = ':'; t = ($ PATH), unset IFS; t = ($ {t [@] %% * usr *}); IFS = ':'; echo "$; {t [*]} ");
nicerobot

1
यह समाधान पथ में पथ के साथ काम नहीं करता है जिसमें रिक्त स्थान होते हैं; यह उन्हें कॉलन द्वारा प्रतिस्थापित करता है।
sschuberth

11

यहां एक-लाइनर है जो वर्तमान स्वीकृत और उच्चतम श्रेणी के उत्तरों के बावजूद , PATH में अदृश्य वर्ण नहीं जोड़ता है और उन रास्तों से सामना कर सकता है जिनमें रिक्त स्थान होते हैं:

export PATH=$(p=$(echo $PATH | tr ":" "\n" | grep -v "/cygwin/" | tr "\n" ":"); echo ${p%:})

व्यक्तिगत रूप से, मुझे यह पढ़ने / समझने में भी आसान लगता है, और इसमें केवल awk का उपयोग करने के बजाय सामान्य आदेश शामिल हैं।


2
... और अगर आप कुछ ऐसा चाहते हैं जो फ़ाइल नाम में नई कहानियों के साथ भी सामना कर सकता है, तो आप इसका उपयोग कर सकते हैं: export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) (हालांकि यकीनन, आप अपने आप से पूछना चाह सकते हैं कि आपको इसकी आवश्यकता क्यों है, अगर आप :))
ehdr

यह आंशिक मैचों को हटा देगा, जो संभवतः वह नहीं है जो आप चाहते हैं; मैं का प्रयोग करेंगे grep -v "^/path/to/remove\$"याgrep -v -x "/path/to/remove"
ShadSterling

ठीक समाधान, लेकिन क्या आपको लगता trहै कि वास्तव में इससे अधिक सामान्य है awk? ;)
के।-माइकल ऐ

1
पूर्ण रूप से। लाइट-वेट वातावरण, जैसे कि विंडोज पर गेट बैश, जैसे इंटरपेंटर की बजाय एक सरल टूल के साथ आते trहैं awk
sschuberth

1
@ehdr: आप प्रतिस्थापित करने की आवश्यकता echo "..."के साथ printf "%s" "..."ऐसा रास्तों पर काम करने के लिए -eऔर इसी तरह की। देखें stackoverflow.com/a/49418406/102441
एरिक

8

यहाँ एक समाधान है कि:

  • शुद्ध बैश है,
  • अन्य प्रक्रियाओं (जैसे 'sed' या 'awk') को लागू नहीं करता है,
  • नहीं बदलता है IFS,
  • एक उप-खोल कांटा नहीं करता है,
  • रिक्त स्थान के साथ पथ संभालती है, और
  • में तर्क की सभी घटनाओं को हटाता है PATH

    removeFromPath () {
       स्थानीय पी.डी.
       पी = ": $ 1:"
       घ = ": $ पथ:"
       d = $ {घ // $ पी /:}
       d = $ {घ / # /}
       पथ = $ {d /%: /}
    }

4
मुझे यह समाधान पसंद है। शायद चर नामों को अधिक वर्णनात्मक बना सकते हैं?
अनुकुल

बहुत अच्छा। हालाँकि, पथ खंडों के लिए काम नहीं करता है जिसमें तारांकन (*) है। कभी-कभी वे गलती से वहां पहुंच जाते हैं।
Jörg

6

समारोह __path_remove () {
स्थानीय D = ": $ {PATH}:";
["$ {D /: $ 1: /:}"! = "$ D"] && पथ = "$ {D /: $ 1: /:}";
पथ = "$ {पथ / # /}";
निर्यात PATH = "$ {PATH /%: /}";
}

यह मेरे .bashrc फ़ाइल से खोदा। जब आप पथ के साथ खेलते हैं, और यह खो जाता है, awk / sed / grep अनुपलब्ध हो जाता है :-)


1
यह बहुत अच्छी बात है। (मैं इस तरह की सरल चीजों के लिए बाहरी उपयोगिताओं को निष्पादित करने का शौक नहीं था)।

6

अब तक मुझे मिला सबसे अच्छा शुद्ध बैश विकल्प निम्नलिखित है:

function path_remove {
  PATH=${PATH/":$1"/} # delete any instances in the middle or at the end
  PATH=${PATH/"$1:"/} # delete any instances at the beginning
}

यह $ PATH में निर्देशिका जोड़ने के लिए बहुत सही उत्तर पर आधारित नहीं है अगर यह सुपरयूज़र पर पहले से ही नहीं है


यह भी काफी अच्छा है। मैंने इसका परीक्षण किया। यदि PATH में एक डुप्लिकेट पथ (उदाहरण के लिए दो समान हैं), तो उनमें से केवल एक को हटा दिया जाता है। आप इसे एक-लाइनर में भी बना सकते हैं:removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

यह समाधान तब विफल हो जाता है जब $PATHलक्ष्य का एक उप-फ़ोल्डर (यानी हटाया जाना) होता है। उदाहरण के लिए: a:abc/def/bin:b-> a/bin:b, जब abc/defहटाया जाना है।
रॉबिन ह्सु

5

मैं बस बैश वितरण में कार्यों का उपयोग कर रहा हूं, जो कि 1991 के बाद से जाहिरा तौर पर किया गया है। ये अभी भी फेडोरा पर बाश-डॉक्स पैकेज में हैं, और इनका उपयोग किया जाता है /etc/profile, लेकिन अब और नहीं ...

$ rpm -ql bash-doc |grep pathfunc
/usr/share/doc/bash-4.2.20/examples/functions/pathfuncs
$ cat $(!!)
cat $(rpm -ql bash-doc |grep pathfunc)
#From: "Simon J. Gerraty" <sjg@zen.void.oz.au>
#Message-Id: <199510091130.VAA01188@zen.void.oz.au>
#Subject: Re: a shell idea?
#Date: Mon, 09 Oct 1995 21:30:20 +1000


# NAME:
#       add_path.sh - add dir to path
#
# DESCRIPTION:
#       These functions originated in /etc/profile and ksh.kshrc, but
#       are more useful in a separate file.
#
# SEE ALSO:
#       /etc/profile
#
# AUTHOR:
#       Simon J. Gerraty <sjg@zen.void.oz.au>

#       @(#)Copyright (c) 1991 Simon J. Gerraty
#
#       This file is provided in the hope that it will
#       be of use.  There is absolutely NO WARRANTY.
#       Permission to copy, redistribute or otherwise
#       use this file is hereby granted provided that
#       the above copyright notice and this notice are
#       left intact.

# is $1 missing from $2 (or PATH) ?
no_path() {
        eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

4

मैंने यहाँ इसका उत्तर लिखा है (awk का उपयोग करके)। लेकिन मुझे यकीन नहीं है कि आप क्या देख रहे हैं? यह कम से कम मुझे स्पष्ट दिखता है कि यह क्या करता है, बजाय एक पंक्ति में फिट होने के। एक साधारण एक लाइनर के लिए, हालांकि, यह केवल सामान निकालता है, मैं सलाह देता हूं

echo $PATH | tr ':' '\n' | awk '$0 != "/bin"' | paste -sd:

की जगह ले रहा है

echo $PATH | tr ':' '\n' | 
    awk '$0 != "/bin"; $0 == "/bin" { print "/bar" }' | paste -sd:

या (कम लेकिन कम पठनीय)

echo $PATH | tr ':' '\n' | awk '$0 == "/bin" { print "/bar"; next } 1' | paste -sd:

वैसे भी, एक ही सवाल के लिए, और बहुत सारे उपयोगी जवाब, यहां देखें ।


और अगर आप उन लाइनों को हटाना चाहते हैं जिनमें आंशिक स्ट्रिंग का उपयोग होता है awk '$0 !~ "/bin"'। यानी ऐसी लाइनें रखें जिनमें awk ऑपरेटर के साथ '/ bin' न हों !~
thoni56

3

वैसे, बैश में, क्योंकि यह नियमित अभिव्यक्ति का समर्थन करता है, मैं बस करूँगा:

PATH=${PATH/:\/home\/user\/bin/}

क्या यह केवल पथप्रदर्शक विस्तार नहीं है, नियमित अभिव्यक्ति नहीं है?
ड्रीमलैक्स

2
हालांकि बैश नियमित भावों का समर्थन करता है (बैश 3 के रूप में), यह इसका उदाहरण नहीं है, यह एक परिवर्तनीय प्रतिस्थापन है।
रॉबर्ट गैंबल

4
यह पैटर्न चर विस्तार है और समाधान में कई समस्याएं हैं। 1) यह पहले तत्व से मेल नहीं खाएगा। 2) यह "/ घर / उपयोगकर्ता / बिन" के साथ शुरू होने वाली किसी भी चीज़ से मेल खाएगा, न कि सिर्फ "/ घर / उपयोगकर्ता / बिन"। 3) इसमें विशेष पात्रों से बचने की आवश्यकता होती है। सबसे अच्छा, मैं कहूंगा कि यह एक अधूरा उदाहरण है।
nicerobot

2

मुझे अपने मूल प्रश्न में @ बेनबैंक के अपडेट में दिखाए गए तीन फ़ंक्शन पसंद हैं। उन्हें सामान्य बनाने के लिए, मैं एक 2-तर्क फॉर्म का उपयोग करता हूं, जो मुझे PATH या किसी अन्य पर्यावरण चर को सेट करने की अनुमति देता है जो मैं चाहता हूं:

path_append ()  { path_remove $1 $2; export $1="${!1}:$2"; }
path_prepend () { path_remove $1 $2; export $1="$2:${!1}"; }
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

उपयोग के उदाहरण:

path_prepend PATH /usr/local/bin
path_append PERL5LIB "$DEVELOPMENT_HOME/p5/src/perlmods"

ध्यान दें कि मैंने ऐसे कुछ उद्धरण चिह्नों को भी जोड़ा है, जिसमें रिक्त स्थान वाले मार्गनामों के उचित प्रसंस्करण की अनुमति है।


2

बाश में $ PATH चर से एक रास्ता निकालने का सबसे सुरुचिपूर्ण तरीका क्या है?

जाग से अधिक सुरुचिपूर्ण क्या है?

path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; 

अजगर! यह एक अधिक पठनीय और बनाए रखने योग्य समाधान है, और यह देखने के लिए निरीक्षण करना आसान है कि यह वास्तव में आप क्या चाहते हैं।

क्या आप पहले पथ तत्व को हटाना चाहते हैं?

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"

(इसके बजाय पाइपिंग से echo, os.getenv['PATH']थोड़ा छोटा होगा, और उपरोक्त के समान परिणाम प्रदान करेगा, लेकिन मुझे चिंता है कि पायथन उस पर्यावरण चर के साथ कुछ कर सकता है, इसलिए संभवतः इसे उस पर्यावरण से सीधे पाइप करना सबसे अच्छा है जिसकी आप परवाह करते हैं ।)

इसी तरह अंत से हटाने के लिए:

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"

इन पुन: प्रयोज्य शेल फ़ंक्शंस को बनाने के लिए, जो आप कर सकते हैं, उदाहरण के लिए, अपनी .bashrc फ़ाइल में चिपकाएँ:

strip_path_first () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"
}

strip_path_last () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"
}

1

हां, PATH के अंत में एक बृहदान्त्र लगा रहा है, उदाहरण के लिए, पथ को थोड़ा कम अनाड़ी और त्रुटि-रहित बनाता है।

path_remove ()  { 
   declare i newPATH
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      #echo ${@:${i}:1}
      newPATH="${newPATH//${@:${i}:1}:/}" 
   done
   export PATH="${newPATH%:}" 
   return 0; 
} 

path_remove_all ()  {
   declare i newPATH
   shopt -s extglob
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//+(${@:${i}:1})*([^:]):/}" 
      #newPATH="${newPATH//+(${@:${i}:1})*([^:])+(:)/}" 
   done
   shopt -u extglob 
   export PATH="${newPATH%:}" 
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 

1

यदि आप $ PATH में डुप्लिकेट को हटाने के बारे में चिंतित हैं , तो सबसे सुरुचिपूर्ण तरीका, IMHO, उन्हें पहली जगह में जोड़ना नहीं होगा। 1 पंक्ति में:

if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$folder" ) ; then PATH=$PATH:$folder ; fi

$ फ़ोल्डर को कुछ भी बदला जा सकता है, और इसमें स्थान हो सकते हैं ("/ घर / उपयोगकर्ता / मेरे दस्तावेज़")


1

सबसे सुंदर शुद्ध बैश समाधान मैंने आज तक पाया है:

pathrm () {                                                                      
  local IFS=':'                                                                  
  local newpath                                                                  
  local dir                                                                      
  local pathvar=${2:-PATH}                                                       
  for dir in ${!pathvar} ; do                                                    
    if [ "$dir" != "$1" ] ; then                                                 
      newpath=${newpath:+$newpath:}$dir                                          
    fi                                                                           
  done                                                                           
  export $pathvar="$newpath"                                                        
}

pathprepend () {                                                                 
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="$1${!pathvar:+:${!pathvar}}"                                  
}

pathappend () {                                                                    
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="${!pathvar:+${!pathvar}:}$1"                                  
} 

1

अन्य सुझाव दिया समाधान के अधिकांश स्ट्रिंग मिलान पर केवल भरोसा करते हैं और युक्त विशेष नाम की तरह पथ के सेगमेंट को ध्यान में रखना नहीं है ., ..या ~। नीचे बैश फ़ंक्शन अपने तर्क और पथ खंडों में डायरेक्टरी स्ट्रिंग्स को हल करता है ताकि लॉजिकल डायरेक्टरी के साथ-साथ स्ट्रिंग मैच का पता लगाया जा सके।

rm_from_path() {
  pattern="${1}"
  dir=''
  [ -d "${pattern}" ] && dir="$(cd ${pattern} && pwd)"  # resolve to absolute path

  new_path=''
  IFS0=${IFS}
  IFS=':'
  for segment in ${PATH}; do
    if [[ ${segment} == ${pattern} ]]; then             # string match
      continue
    elif [[ -n ${dir} && -d ${segment} ]]; then
      segment="$(cd ${segment} && pwd)"                 # resolve to absolute path
      if [[ ${segment} == ${dir} ]]; then               # logical directory match
        continue
      fi
    fi
    new_path="${new_path}${IFS}${segment}"
  done
  new_path="${new_path/#${IFS}/}"                       # remove leading colon, if any
  IFS=${IFS0}

  export PATH=${new_path}
}

परीक्षा:

$ mkdir -p ~/foo/bar/baz ~/foo/bar/bif ~/foo/boo/bang
$ PATH0=${PATH}
$ PATH=~/foo/bar/baz/.././../boo/././../bar:${PATH}  # add dir with special names
$ rm_from_path ~/foo/boo/../bar/.  # remove same dir with different special names
$ [ ${PATH} == ${PATH0} ] && echo 'PASS' || echo 'FAIL'

बॉक्स के बाहर के लिए एक प्लस
मार्टिन यॉर्क

1

स्क्रैच से लिनक्स तीन बैश कार्यों को परिभाषित करता है /etc/profile:

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
        local IFS=':'
        local NEWPATH
        local DIR
        local PATHVARIABLE=${2:-PATH}
        for DIR in ${!PATHVARIABLE} ; do
                if [ "$DIR" != "$1" ] ; then
                  NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
                fi
        done
        export $PATHVARIABLE="$NEWPATH"
}

pathprepend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}

pathappend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}

export -f pathremove pathprepend pathappend

Ref: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html


1

मुझे पता है कि यह प्रश्न BASH के बारे में पूछता है, जिसे सभी को पसंद करना चाहिए, लेकिन चूंकि मुझे समरूपता का आनंद मिलता है और कभी-कभी मुझे "csh" का उपयोग करना पड़ता है, इसलिए मैंने "path_prepend ()", "path_append ()" और "path_remove" के बराबर बनाया () "ऊपर सुरुचिपूर्ण समाधान।

गिस्ट यह है कि "csh" में फ़ंक्शन नहीं होते हैं, इसलिए मैंने अपनी व्यक्तिगत बिन निर्देशिका में बहुत कम शेल स्क्रिप्ट रखी है जो फ़ंक्शन की तरह कार्य करती है। मैं नामित वातावरण चर परिवर्तन करने के लिए उन लिपियों को स्रोत बनाने के लिए उपनाम बनाता हूं।

~ / Bin / _path_remove.csh:

set _resolve = `eval echo $2`
setenv $1 `eval echo -n \$$1 | awk -v RS=: -v ORS=: '$1 != "'${_resolve}'"' | sed 's/:$//'`;
unset _resolve

~ / Bin / _path_append.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_base}:${_resolve}
unset _base _resolve

~ / Bin / _path_prepend.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_resolve}:${_base}
unset _base _resolve

~ / Bin / .cshrc:


alias path_remove  "source ~/bin/_path_remove.csh  '\!:1' '\!:2'"
alias path_append  "source ~/bin/_path_append.csh  '\!:1' '\!:2'"
alias path_prepend "source ~/bin/_path_prepend.csh '\!:1' '\!:2'"

आप उन्हें इस तरह से उपयोग कर सकते हैं ...

%(csh)> path_append MODULEPATH ${HOME}/modulefiles

0

चूँकि यह काफी समस्याग्रस्त है, जैसा कि वहाँ कोई सुंदर तरीका नहीं है, मैं इस समस्या को हल करने से बचने की सलाह देता हूं: इसे फाड़ने के प्रयास के बजाय अपने पैट का निर्माण करें।

मैं और अधिक विशिष्ट हो सकता है अगर मुझे आपकी वास्तविक समस्या का संदर्भ पता हो। अंतरिम में, मैं संदर्भ के रूप में एक सॉफ्टवेयर बिल्ड का उपयोग करूंगा।

सॉफ़्टवेयर बिल्ड के साथ एक आम समस्या यह है कि यह कुछ मशीनों पर टूट जाता है, आखिरकार किसी ने अपने डिफ़ॉल्ट शेल (PATH और अन्य पर्यावरण चर) को कैसे कॉन्फ़िगर किया है। सुरुचिपूर्ण समाधान शेल वातावरण को पूरी तरह से निर्दिष्ट करके अपनी बिल्ड स्क्रिप्ट को प्रतिरक्षा बनाने के लिए है। पैठ और अन्य पर्यावरण चर सेट करने के लिए अपनी बिल्ड स्क्रिप्ट को कोड करें जो आपके द्वारा नियंत्रित किए जाने वाले टुकड़ों पर आधारित हों, जैसे कि कंपाइलर, लाइब्रेरी, टूल्स, कंपोनेंट्स आदि का स्थान। प्रत्येक कंफर्टेबल आइटम को कुछ ऐसा बनाएं जिसे आप व्यक्तिगत रूप से सेट, वेरिफाई कर सकें, और फिर अपनी स्क्रिप्ट में उचित रूप से उपयोग करें।

उदाहरण के लिए, मेरे पास एक मावेन-आधारित वेबलॉजिक-लक्षित जावा बिल्ड है जो मुझे अपने नए नियोक्ता से विरासत में मिला है। बिल्ड स्क्रिप्ट नाजुक होने के लिए कुख्यात है, और एक और नया कर्मचारी और मैंने तीन हफ्ते बिताए (पूरा समय नहीं, बस यहां और वहां, लेकिन अभी भी कई घंटे) इसे हमारी मशीनों पर काम करने के लिए मिल रहा है। एक आवश्यक कदम यह था कि मैंने PATH को अपने नियंत्रण में ले लिया ताकि मुझे पता चल सके कि कौन सा जावा, कौन सा मावेन और कौन सा वेबलॉजिक इनवॉइस किया जा रहा है। मैंने उन प्रत्येक टूल को इंगित करने के लिए पर्यावरण चर बनाए, फिर मैंने उन प्लस के आधार पर कुछ अन्य लोगों की गणना की। इसी तरह की तकनीकों ने अन्य विन्यास योग्य सेटिंग्स का नाम दिया, जब तक कि हमने अंत में एक प्रतिलिपि प्रस्तुत करने योग्य निर्माण नहीं किया।

वैसे, मावेन का उपयोग न करें, जावा ठीक है, और केवल वेबलॉजिक खरीदें अगर आपको इसकी क्लस्टरिंग की आवश्यकता है (लेकिन अन्यथा नहीं, और विशेष रूप से इसकी मालिकाना विशेषताएं नहीं)।

शुभकामनाएँ।


कभी-कभी आपके पास रूट एक्सेस नहीं होता है और आपका व्यवस्थापक आपका प्रबंधन करता है PATH। निश्चित रूप से, आप अपना निर्माण कर सकते हैं, लेकिन हर बार जब आपका व्यवस्थापक कुछ ऐसा करता है तो आपको यह पता लगाना होगा कि उसने इसे कहां रखा है। उस तरह के एक व्यवस्थापक होने के उद्देश्य को हराया।
शीप

0

@ लेटब के साथ, मैंने प्रश्न का उत्तर देने में योगदान दिया " मैं शेल स्क्रिप्ट में $ PATH तत्वों का हेरफेर कैसे करूं ", इसलिए मेरा मुख्य उत्तर है।

bashअन्य बॉर्न शेल डेरिवेटिव में 'स्प्लिट' कार्यक्षमता सबसे $IFSअंतर-क्षेत्र विभाजक के साथ प्राप्त की जाती है । उदाहरण के लिए, स्थितीय तर्कों (स्थापित करने के लिए $1, $2, ...) पथ, उपयोग के तत्वों रहे हैं:

set -- $(IFS=":"; echo "$PATH")

यह तब तक ठीक रहेगा जब तक $ PATH में रिक्त स्थान न हों। रिक्त स्थान वाले पथ तत्वों के लिए इसे बनाना एक गैर-तुच्छ अभ्यास है - इच्छुक पाठक के लिए छोड़ दिया गया। शायद पर्ल जैसी स्क्रिप्टिंग भाषा का उपयोग करके इससे निपटना सरल है।

मेरे पास एक स्क्रिप्ट भी है clnpath, जिसका उपयोग मैं अपने पैट को स्थापित करने के लिए बड़े पैमाने पर करता हूं। मैंने इसे " कैश में पाथ चर की नकल से कैसे रखा जाए " के जवाब में प्रलेखित किया ।


IFS =: a = ($ PATH); IFS = विभाजन भी अच्छा है। काम करता है अगर वे रिक्त स्थान भी शामिल है। लेकिन फिर आपको एक सरणी मिली, और लूप्स के लिए फिडेल करना होगा और इस तरह के नामों को हटाना होगा।
जोहान्स शाउब -

हाँ; यह पूरी तरह से हो जाता है - मेरी अद्यतन टिप्पणी के साथ, इस बिंदु पर स्क्रिप्टिंग भाषा का उपयोग करना शायद सरल है।
जोनाथन लेफ़लर

0

इस समस्या को कष्टप्रद बना देता है जो पहले और अंतिम तत्वों में से एक है। आईएफएस को बदलकर और एक सरणी का उपयोग करके समस्या को हल किया जा सकता है, लेकिन मुझे पता नहीं है कि पथ को सरणी रूप में परिवर्तित करने के बाद कोलन को फिर से कैसे पेश किया जाए।

यहां थोड़ा कम सुरुचिपूर्ण संस्करण है जो $PATHकेवल स्ट्रिंग हेरफेर का उपयोग करने से एक निर्देशिका को निकालता है । मैंने इसका परीक्षण किया है।

#!/bin/bash
#
#   remove_from_path dirname
#
#   removes $1 from user's $PATH

if [ $# -ne 1 ]; then
  echo "Usage: $0 pathname" 1>&2; exit 1;
fi

delendum="$1"
NEWPATH=
xxx="$IFS"
IFS=":"
for i in $PATH ; do
  IFS="$xxx"
  case "$i" in
    "$delendum") ;; # do nothing
    *) [ -z "$NEWPATH" ] && NEWPATH="$i" || NEWPATH="$NEWPATH:$i" ;;
  esac
done

PATH="$NEWPATH"
echo "$PATH"

0

यहाँ एक पर्ल वन-लाइनर है:

PATH=`perl -e '$a=shift;$_=$ENV{PATH};s#:$a(:)|^$a:|:$a$#$1#;print' /home/usr/bin`

$aचर पथ हटा दिया जाना चाहिए हो जाता है। s(विकल्प) और printआदेशों परोक्ष पर काम $_चर।


0

यहाँ अच्छा सामान। मैं इसका उपयोग पहली जगह में दुपट्टे को जोड़ने से रखने के लिए करता हूं।

#!/bin/bash
#
######################################################################################
#
# Allows a list of additions to PATH with no dupes
# 
# Patch code below into your $HOME/.bashrc file or where it
# will be seen at login.
#
# Can also be made executable and run as-is.
#
######################################################################################

# add2path=($HOME/bin .)                  ## uncomment space separated list 
if [ $add2path ]; then                    ## skip if list empty or commented out
for nodup in ${add2path[*]}
do
    case $PATH in                 ## case block thanks to MIKE511
    $nodup:* | *:$nodup:* | *:$nodup ) ;;    ## if found, do nothing
    *) PATH=$PATH:$nodup          ## else, add it to end of PATH or
    esac                          ## *) PATH=$nodup:$PATH   prepend to front
done
export PATH
fi
## debug add2path
echo
echo " PATH == $PATH"
echo

1
आप PATH स्ट्रिंग में एक अग्रणी और अनुगामी बृहदान्त्र जोड़कर अपने केस स्टेटमेंट को सरल बना सकते हैं:case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
Glenn jackman

0

विस्तारित ग्लोबिंग सक्षम होने के साथ यह निम्न करना संभव है:

# delete all /opt/local paths in PATH
shopt -s extglob 
printf "%s\n" "${PATH}" | tr ':' '\n' | nl
printf "%s\n" "${PATH//+(\/opt\/local\/)+([^:])?(:)/}" | tr ':' '\n' | nl 

man bash | less -p extglob

0

विस्तारित ग्लोबिंग वन-लाइनर (अच्छी तरह से):

path_remove ()  { shopt -s extglob; PATH="${PATH//+(${1})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

लगता है $ 1 में स्लैश से बचने की कोई आवश्यकता नहीं है।

path_remove ()  { shopt -s extglob; declare escArg="${1//\//\\/}"; PATH="${PATH//+(${escArg})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

0

PATH में कॉलोन जोड़ना हम भी कुछ ऐसा कर सकते हैं:

path_remove ()  { 
   declare i newPATH
   # put a colon at the beginning & end AND double each colon in-between
   newPATH=":${PATH//:/::}:"   
   for ((i=1; i<=${#@}; i++)); do
       #echo ${@:${i}:1}
       newPATH="${newPATH//:${@:${i}:1}:/}"   # s/:\/fullpath://g
   done
   newPATH="${newPATH//::/:}"
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 


path_remove_all ()  {
   declare i newPATH extglobVar
   extglobVar=0
   # enable extended globbing if necessary
   [[ ! $(shopt -q extglob) ]]  && { shopt -s extglob; extglobVar=1; }
   newPATH=":${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"     # s/:\/path[^:]*//g
   done
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   # disable extended globbing if it was enabled in this function
   [[ $extglobVar -eq 1 ]] && shopt -u extglob
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 

0

Path_remove_all में (proxxy द्वारा):

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}" 
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g 

0

हालांकि यह एक बहुत पुराना धागा है, मैंने सोचा कि यह समाधान ब्याज का हो सकता है:

PATH="/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
REMOVE="ccache" # whole or part of a path :)
export PATH=$(IFS=':';p=($PATH);unset IFS;p=(${p[@]%%$REMOVE});IFS=':';echo "${p[*]}";unset IFS)
echo $PATH # outputs /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

यह इस ब्लॉग पोस्ट पर पाया । मुझे लगता है कि मुझे यह सबसे ज्यादा पसंद है :)


0

मैंने ज्यादातर लोगों की तुलना में थोड़ा अलग दृष्टिकोण लिया और विशेष रूप से सिर्फ स्ट्रिंग हेरफेर पर ध्यान केंद्रित किया, जैसे:

path_remove () {
    if [[ ":$PATH:" == *":$1:"* ]]; then
        local dirs=":$PATH:"
        dirs=${dirs/:$1:/:}
        export PATH="$(__path_clean $dirs)"
    fi
}
__path_clean () {
    local dirs=${1%?}
    echo ${dirs#?}
}

उपरोक्त मेरे द्वारा उपयोग किए जाने वाले अंतिम कार्यों का एक सरल उदाहरण है। मैंने भी PATH में पहले से निर्दिष्ट पथ के बाद / बाद में एक पथ डालने path_add_beforeऔर बनाने की path_add_afterअनुमति दी है।

मेरे dotfiles में path_helpers.sh में फ़ंक्शन का पूरा सेट उपलब्ध है । वे PATH स्ट्रिंग की शुरुआत / मध्य / अंत में हटाने / लागू करने / प्रीपेडिंग / डालने का पूरा समर्थन करते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.