ما هي الطريقة الأكثر أناقة لإزالة المسار من متغير PATH $ في Bash؟

StackOverflow https://stackoverflow.com/questions/370047

سؤال

أو بشكل أكثر عمومية، كيف يمكنني إزالة عنصر من قائمة مفصولة بنقطتين في متغير بيئة Bash؟

اعتقدت أنني رأيت طريقة بسيطة للقيام بذلك منذ سنوات، باستخدام الأشكال الأكثر تقدمًا لتوسيع متغير Bash، ولكن إذا كان الأمر كذلك فقد فقدت مسارها.أدى البحث السريع في 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)

ألا يوجد شيء مباشر؟هل هناك أي شيء مشابه لوظيفة Split() في Bash؟

تحديث:
يبدو أنني بحاجة إلى الاعتذار عن سؤالي الغامض عمدًا؛لم أكن مهتمًا بحل حالة استخدام محددة بقدر اهتمامي بإثارة مناقشة جيدة.لحسن الحظ، حصلت عليه!

هناك بعض التقنيات الذكية جدًا هنا.وفي النهاية، أضفت الوظائف الثلاث التالية إلى صندوق الأدوات الخاص بي.يحدث السحر في path_remove، والذي يعتمد إلى حد كبير على استخدام مارتن يورك الذكي لـ awkمتغير RS .

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/:$//'`; }

الشيء الحقيقي الوحيد الموجود هناك هو الاستخدام sed لإزالة القولون زائدة.وبالنظر إلى مدى وضوح بقية حل مارتن، فأنا على استعداد تام للتعايش معه!


سؤال ذو صلة: كيف يمكنني التعامل مع عناصر PATH $ في البرامج النصية لـ Shell؟

هل كانت مفيدة؟

المحلول

دقيقة مع اوك:

# 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/:*$//')

يؤدي هذا إلى إزالة أي علامات نقطية زائدة متبقية عن طريق حذف الإدخالات الأخيرة، مما قد يؤدي إلى إضافتها بشكل فعال . إلى طريقك.

نصائح أخرى

اختراقي القذر:

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

نظرًا لأن المشكلة الكبيرة في الاستبدال هي الحالات النهائية، فماذا عن جعل الحالات النهائية لا تختلف عن الحالات الأخرى؟إذا كان المسار يحتوي بالفعل على نقطتين في البداية والنهاية، فيمكننا ببساطة البحث عن السلسلة المطلوبة المغلفة بالنقطتين.كما هو الحال، يمكننا بسهولة إضافة تلك النقطتين وإزالتها بعد ذلك.

# 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

باش خالص :).

إليك الحل الأبسط الذي يمكنني ابتكاره:

#!/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*" بـ "/home/user/bin" لإزالة هذا العنصر فقط.

تحديث لكل com.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[*]}"

وهنا بطانة واحدة، على الرغم من التيار قبلت و أعلى تصنيف الإجابات، لا تضيف أحرفًا غير مرئية إلى PATH ويمكنها التعامل مع المسارات التي تحتوي على مسافات:

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

شخصيًا، أجد أيضًا أن هذا سهل القراءة/الفهم، ولا يتضمن سوى الأوامر الشائعة بدلاً من استخدام awk.

هنا الحل الذي:

  • خالص باش
  • لا يستدعي عمليات أخرى (مثل 'sed' أو 'awk')،
  • لم يتغير IFS,
  • لا تفرع قذيفة فرعية ،
  • يتعامل مع المسارات بمسافات، و
  • يزيل كافة تكرارات الوسيطة في PATH.

    removeFromPath() {
       local p d
       p=":$1:"
       d=":$PATH:"
       d=${d//$p/:}
       d=${d/#:/}
       PATH=${d/%:/}
    }

وظيفة __path_remove(){
محلي D = ":${PATH}:"؛
[ "${D/:$1:/:}" != "$D" ] && PATH="${D/:$1:/:}";
PATH="${PATH/#:/}";
تصدير PATH="${PATH/%:/}";
}

استخرجه من ملف .bashrc الخاص بي.عندما تتلاعب بـ PATH، وتضيع، يصبح awk/sed/grep غير متاح :-)

أفضل خيار bash خالص وجدته حتى الآن هو ما يلي:

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 إذا لم يكن موجودًا بالفعل على المستخدم الخارق.

لقد كنت أستخدم للتو الوظائف الموجودة في توزيعة bash، والتي كانت موجودة على ما يبدو منذ عام 1991.لا تزال هذه العناصر موجودة في حزمة bash-docs على Fedora، ويتم استخدامها في /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;:\$;;"`
}

لقد كتبت إجابة على هذا هنا (باستخدام 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:

على أية حال، لنفس السؤال، والكثير من الإجابات المفيدة، انظر هنا.

حسنًا، في bash، لأنه يدعم التعبير العادي، سأفعل ببساطة:

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

ما هي الطريقة الأكثر أناقة لإزالة المسار من متغير PATH $ في Bash؟

ما هو أكثر أناقة من awk؟

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

نعم، وضع نقطتين في نهاية 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 

إذا كنت قلقًا بشأن الإزالة التكرارات في $PATH، الطريقة الأكثر أناقة، IMHO، هي عدم إضافتها في المقام الأول.في سطر واحد:

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

يمكن استبدال $folder بأي شيء، وقد يحتوي على مسافات ("/home/user/my Documents")

حل باش النقي الأكثر أناقة الذي وجدته حتى الآن:

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

تعتمد معظم الحلول المقترحة الأخرى فقط على مطابقة السلسلة ولا تأخذ في الاعتبار أجزاء المسار التي تحتوي على أسماء خاصة مثل ., .., ، أو ~.تعمل وظيفة bash أدناه على حل سلاسل الدليل في وسيطتها وفي مقاطع المسار للعثور على مطابقات الدليل المنطقي بالإضافة إلى مطابقات السلسلة.

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'

يعرّف Linux من Scratch ثلاث وظائف Bash في /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

المرجع: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html

تعجبني الوظائف الثلاث الموضحة في تحديث @BenBlank لسؤاله الأصلي.لتعميمها، أستخدم نموذجًا مكونًا من وسيطتين، مما يسمح لي بتعيين 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"

لاحظ أنني أضفت أيضًا بعض علامات الاقتباس للسماح بالمعالجة الصحيحة لأسماء المسارات التي تحتوي على مسافات.

نظرًا لأن هذا يميل إلى أن يكون مشكلة كبيرة، حيث لا توجد طريقة أنيقة، أوصي بتجنب المشكلة عن طريق إعادة ترتيب الحل:قم ببناء المسار الخاص بك بدلاً من محاولة هدمه.

يمكن أن أكون أكثر تحديدًا إذا كنت أعرف سياق مشكلتك الحقيقية.في غضون ذلك، سأستخدم بناء البرنامج كسياق.

إحدى المشاكل الشائعة في إنشاء البرامج هي أنها تتعطل على بعض الأجهزة، ويرجع ذلك في النهاية إلى كيفية تكوين شخص ما لقشرته الافتراضية (PATH ومتغيرات البيئة الأخرى).الحل الأنيق هو جعل نصوص البناء الخاصة بك محصنة من خلال تحديد بيئة الصدفة بشكل كامل.قم بترميز البرامج النصية للبناء الخاص بك لتعيين PATH ومتغيرات البيئة الأخرى بناءً على أجزاء التجميع التي تتحكم فيها، مثل موقع المترجم والمكتبات والأدوات والمكونات وما إلى ذلك.اجعل كل عنصر قابل للتكوين شيئًا يمكنك تعيينه بشكل فردي والتحقق منه ثم استخدامه بشكل مناسب في البرنامج النصي الخاص بك.

على سبيل المثال، لدي إصدار Java يستهدف WebLogic يستند إلى Maven وقد ورثته من صاحب العمل الجديد.يشتهر نص البناء بكونه هشًا، وقد أمضيت أنا وموظف جديد آخر ثلاثة أسابيع (ليس بدوام كامل، فقط هنا وهناك، ولكن لا يزال هناك عدة ساعات) في جعله يعمل على أجهزتنا.كانت الخطوة الأساسية هي أنني سيطرت على PATH حتى أعرف بالضبط أي Java، وأي Maven، وأي WebLogic تم استدعاؤه.لقد قمت بإنشاء متغيرات بيئة للإشارة إلى كل من هذه الأدوات، ثم حسبت PATH بناءً على تلك المتغيرات بالإضافة إلى عدد قليل من الأدوات الأخرى.قامت تقنيات مماثلة بترويض الإعدادات الأخرى القابلة للتكوين، حتى أنشأنا في النهاية بنية قابلة للتكرار.

بالمناسبة، لا تستخدم Maven، Java لا بأس بها، ولا تشتري WebLogic إلا إذا كنت في حاجة ماسة إلى تجميعاته (لكن بخلاف ذلك لا، لا سيما ميزاته الخاصة).

أطيب التمنيات.

كما هو الحال مع @litb، ساهمت في الإجابة على السؤال "كيف يمكنني التعامل مع عناصر PATH $ في البرامج النصية لـ Shell"، لذا فإن إجابتي الرئيسية موجودة.

وظيفة "التقسيم" في bash ومشتقات Bourne Shell الأخرى يتم تحقيقها بدقة أكبر باستخدام $IFS, ، الفاصل بين الحقول.على سبيل المثال، لتعيين الوسائط الموضعية ($1, $2, ، ...) لعناصر PATH، استخدم:

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

سيعمل بشكل جيد طالما لا توجد مسافات في $PATH.إن جعل الأمر يعمل مع عناصر المسار التي تحتوي على مسافات هو تمرين غير تافه - متروك للقارئ المهتم.ربما يكون من الأسهل التعامل معها باستخدام لغة برمجة نصية مثل Perl.

لدي أيضاً سكريبت clnpath, ، والذي أستخدمه على نطاق واسع لإعداد PATH الخاص بي.وقد وثقته في الرد على "كيفية تجنب تكرار متغير PATH في csh".

ما يجعل هذه المشكلة مزعجة هي حالات السياج بين العناصر الأولى والأخيرة.يمكن حل المشكلة بشكل أنيق عن طريق تغيير IFS واستخدام مصفوفة، لكنني لا أعرف كيفية إعادة تقديم النقطتين بمجرد تحويل المسار إلى نموذج مصفوفة.

إليك نسخة أقل أناقة قليلاً تزيل دليلاً واحدًا من $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"

إليك سطر واحد من لغة Perl:

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

ال $a المتغير يحصل على المسار المراد إزالته.ال s (بديل) و print الأوامر تعمل ضمنا على $_ عامل.

أشياء جيدة هنا.أستخدم هذا لتجنب إضافة المغفلين في المقام الأول.

#!/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

مع تمكين اللمعان الممتد، من الممكن القيام بما يلي:

# 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

بطانة واحدة ممتدة ممتدة (حسنًا، نوعًا ما):

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

بإضافة النقطتين إلى 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 

في path_remove_all (بواسطة الوكيل):

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

على الرغم من أن هذا موضوع قديم جدًا، إلا أنني أعتقد أن هذا الحل قد يكون مفيدًا:

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

وجدته على هذا مشاركة مدونة.أعتقد أنني أحب هذا أكثر :)

لقد اتبعت نهجًا مختلفًا قليلًا عن معظم الأشخاص هنا وركزت بشكل خاص على معالجة السلسلة فقط، كما يلي:

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_add_before و path_add_after مما يسمح لك بإدراج مسار قبل/بعد مسار محدد بالفعل في PATH.

المجموعة الكاملة من الوظائف متوفرة في path_helpers.sh في ملفات نقطية.إنها تدعم بشكل كامل إزالة/إلحاق/تعليق/إدراج في بداية/وسط/نهاية سلسلة PATH.

السبب في زائدة ':' هو أنك تقوم بتعيين نهاية السطر، وليس الفاصل.أستخدم وحدات محدودة الموارد وأرغب في تجميع كل شيء في برنامج نصي واحد، بدون هذه الشذوذات:

path_remove () {
    PATH="$(echo -n $PATH | awk -v RS=: -v ORS= '$0 != "'$1'"{print s _ $0;s=":"}')"
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top