سؤال إضافة دليل إلى PATH $ إذا لم يكن موجودًا بالفعل


هل كتب أي شخص وظيفة bash لإضافة دليل إلى PATH $ فقط إذا لم يكن موجودًا بالفعل؟

عادة ما أضيف إلى PATH باستخدام ما يلي:

export PATH=/usr/local/mysql/bin:$PATH

إذا قمت بإنشاء PATH الخاص بي في .bash_profile ، فلن يتم قراءته إلا إذا كانت الجلسة التي أقوم بها هي جلسة تسجيل دخول - وهي ليست دائمًا صحيحة. إذا قمت بإنشاء PATH الخاص بي في .bashrc ، فإنه يعمل مع كل subshell. لذا إذا قمت بتشغيل نافذة طرفية ثم شغلت الشاشة ثم قمت بتشغيل برنامج نصي خاص بـ shell ، أحصل على:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

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


116
2017-09-11 16:19


الأصل


نرى stackoverflow.com/questions/273909/... لبعض البنى التحتية التي يمكن أن تساعد. - dmckee
unix.stackexchange.com/questions/4965/... - Ciro Santilli 新疆改造中心 六四事件 法轮功
إذا وضعت إطارًا للمشكلة على أنها "تضيف فقط إذا لم تكن موجودة بالفعل" ، فستفاجأ بوقاحة عندما يحين الوقت الذي يكون فيه العنصر المدرج مهمًا في البداية ولكنه لا ينتهي إلى هناك. ومن أفضل النهج هو إدراج العنصر ، ثم إزالة العناصر المكررة ، فإذا كان الإدخال الجديد موجودًا بالفعل ، فسيتم نقله فعليًا إلى البداية. - Don Hatch


الأجوبة:


من .bashrc الخاص بي:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

لاحظ أنه يجب أن يتم وضع علامة PATH بالفعل على أنها تم تصديرها ، لذا لا يلزم إعادة التصدير. يقوم هذا بالتحقق مما إذا كان الدليل موجودًا أم أنه دليل قبل إضافته ، وهو ما قد لا تهتم به.

أيضاً ، هذا يضيف الدليل الجديد إلى نهاية المسار؛ لوضعه في البداية ، واستخدام PATH="$1${PATH:+":$PATH"}" بدلا من ما سبق PATH= خط.


117
2017-09-12 03:08



يهمني. - Dennis Williamson
@ نيل: إنه يعمل ، لأنه يقارن ":$PATH:" بدلا من مجرد "$PATH" - Gordon Davisson
GordonDavisson: أعتذر ، كان اختباري خاطئًا وكنت على صواب. - Neil
GordonDavisson ما هي نقطة الاشياء في الأقواس المتعرجة. لا أستطيع أن أبدو لأحيرها "${PATH:+"$PATH:"}$ 1 " - boatcoder
@ Mark0978: هذا ما فعلته لإصلاح المشكلة أشار bukzor. ${variable:+value} يعني التحقق ما إذا كان variable تم تعريفها ولها قيمة غير فارغة ، وإذا كانت تعطي نتيجة التقييم value. في الأساس ، إذا كان PATH غير صافٍ للبدء في تعيينه إلى "$PATH:$1". إذا كان فارغًا ، فسيتم تعيينه إلى فقط "$1" (لاحظ عدم وجود القولون). - Gordon Davisson


بالتوسع في الإجابة على جوردون دافيسون ، هذا يدعم الحجج المتعددة

pathappend() {
  for ARG in "$@"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

حتى تتمكن من القيام pathappend path1 path2 path3 path3 ...

للإضافة ،

pathprepend() {
  for ((i=$#; i>0; i--)); 
  do
    ARG=${!i}
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="$ARG${PATH:+":$PATH"}"
    fi
  done
}

على غرار pathappend ، يمكنك القيام به

pathprepend path1 path2 path3 path3 ...


19
2018-05-15 18:42



هذا عظيم! لقد قمت بتغيير واحد صغير بالنسبة لوظيفة "pathprepend" ، من السهل قراءة الحجج في الاتجاه المعاكس ، حتى يمكنك ، على سبيل المثال ، pathprepend P1 P2 P3 وينتهي مع PATH=P1:P2:P3. للحصول على هذا السلوك ، قم بالتغيير for ARG in "$@" do إلى for ((i=$#; i>0; i--)); do ARG=${!i} - ishmael
شكراishmael ، اقتراح جيد ، قمت بتحرير الجواب. أدرك أن تعليقك قد مضى على أكثر من عامين ، لكني لم أعود منذ ذلك الوقت. لا بد لي من معرفة كيفية الحصول على رسائل البريد الإلكتروني تبادل المكدس للهبوط في صندوق البريد الوارد الخاص بي! - Guillaume Perrault-Archambault


هنا شيء من إجابتي إلى هذا السؤال جنبا إلى جنب مع هيكل وظيفة دوج هاريس. يستخدم تعبيرات Bash العادية:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}

12
2017-09-11 19:11



هذا العمل بالنسبة لي فقط باستخدام $1 بدلا من ${1} - Andrei
Andrei: نعم ، الأقواس غير ضرورية في هذه الحالة. لست متأكدًا من سبب إدراجها. - Dennis Williamson


ضع هذا في التعليقات على الإجابة المحددة ، ولكن لا يبدو أن التعليقات تدعم تنسيق PRE ، لذلك أضف الإجابة هنا:

@ gordon-davisson أنا لست من أكبر المعجبين بالاقتباس والتسجيل غير الضروريين. إذا افترضنا أنك تستخدم إصدار bash> = 3 ، فيمكنك بدلاً من ذلك استخدام bash المضمّن في regexs والقيام بما يلي:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

هذا صحيح معالجة الحالات حيث توجد مسافات في الدليل أو PATH. هناك بعض التساؤلات حول ما إذا كان محرك bash المدمج في محرك regex بطيء بما يكفي لأن هذا قد يكون صافي أقل كفاءة من سلسلة السلسلة والاستيفاء الداخلي الذي تقوم به نسختك ، لكن بطريقة ما يبدو أكثر جمالية نظيفة بالنسبة لي.


10
2018-03-02 18:20



دعم التعليقات formatting using the backtick فقط ولكنك لا تحصل على أي تحكم الفقرة لائق. - boatcoder
هذا يضع الإضافة في النهاية. غالبًا ما يُفضل إضافة إلى البداية لتجاوز المواقع الحالية. - Dennis Williamson
DennisWilliamson هذه نقطة عادلة ، على الرغم من أنني لا أوصي بهذا السلوك الافتراضي. ليس من الصعب معرفة كيفية تغيير الموعد النهائي. - Christopher Smith
تضمين التغريدة ردًا علىChristopherSmith unnecessary quoting يعني أنك تعرف في وقت مبكر ذلك $PATH هو ليس لاشيء. "$PATH" يجعلها موافق سواء PATH هو لاغي أو لا. بالمثل إذا $1 يحتوي على أحرف قد تؤدي إلى إرباك محلل الأمر. وضع التعابير المنطقية بين علامتي اقتباس "(^|:)$1(:|$)" يمنع ذلك. - Jesse Chisholm
@ جيسيتشيزولم: في الواقع ، أعتقد أن نقطة كريستوفر هي أن القواعد مختلفة [[ و ]]. أفضل أن أقتبس كل ما يمكن أن يكون من الممكن اقتباسه ، ما لم يتسبب ذلك في الفشل ، ولكني أعتقد أنه على حق ، وأن هذه المقولات ليست فعلاً بحاجة حول $PATH. من ناحية أخرى ، يبدو لي أنك على صواب $1. - Scott


idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

عندما تحتاج إلى HOME / bin $ لتظهر بالضبط مرة واحدة في بداية PATH $ الخاص بك وفي أي مكان آخر ، لا تقبل أي بدائل.


6
2017-08-17 13:31



شكرا ، هذا هو حل أنيق لطيف ، لكنني وجدت أنه كان علي القيام به PATH=${PATH/"... عوضا عن PATH=${PATH//"... من أجل الحصول عليها للعمل. - Mark Booth
يجب أن يتطابق نموذج القطع المزدوج مع أي عدد من التطابقات ؛ الخط المائل الوحيد يطابق الأول فقط (ابحث عن "استبدال النمط" في صفحة رجل باش). غير متأكد من سبب عدم نجاحه ... - andybuckley
هذا يفشل في حالة غير عادية ذلك $1 هو الإدخال الوحيد (بدون علامات). يتم مضاعفة الإدخال. - Dennis Williamson
كما أنه يحذف بقوة أيضا كما أشار إليه PeterS6g. - Dennis Williamson


إليك حل بديل يتمتع بميزة إضافية تتمثل في إزالة الإغراءات المتكررة:

function pathadd {
    PATH=:$PATH
    PATH=$1${PATH//:$1/}
}

يتم prepended الوسيطة واحدة إلى هذه الدالة إلى PATH ثم تتم إزالة المثيل الأول من نفس السلسلة من المسار الموجود. بمعنى آخر ، إذا كان الدليل موجودًا بالفعل في المسار ، فسيتم ترقيته إلى الواجهة بدلاً من إضافته كنسخة مكررة.

تعمل الدالة عن طريق إحاطة نقطتين إلى المسار لضمان أن جميع الإدخالات تحتوي على نقطتين في المقدمة ، ثم تقوم بترحيل الإدخال الجديد إلى المسار الموجود مع إزالة هذا الإدخال. يتم تنفيذ الجزء الأخير باستخدام باش ${var//pattern/sub} الرموز؛ نرى دليل باش لمزيد من التفاصيل.


6
2018-01-21 12:29



تفكير جيد؛ التنفيذ المعيب. فكر فيما يحدث إذا كان لديك بالفعل /home/robert في حياتك PATH وأنت أيضا pathadd /home/rob. - Scott


هنا لي (أعتقد أنها كانت مكتوبة منذ سنوات من قبل أوسكار ، مسؤول النظام القديم في معملتي القديمة ، وكلها معتمدة عليه) ، كانت موجودة في الباشرك لسنوات. لديه فائدة إضافية تتمثل في السماح لك بإضافة الدليل الجديد أو إلحاقه حسب الرغبة:

pathmunge () {
        if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

الاستعمال:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/

5
2017-08-17 18:57





للإعطاء المسبق ، أحب حل @ Russell ، ولكن هناك خطأ صغير: إذا حاولت إضافة شيء مثل "/ bin" إلى مسار "/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin "يستبدل" / bin: "ثلاث مرات (عندما لم تتطابق تمامًا). الجمع بين إصلاح لذلك مع حل الإلحاق من @ غوردون-دافيسون ، أحصل على هذا:

path_prepend() {
    if [ -d "$1" ]; then
        PATH=${PATH//":$1:"/:} #delete all instances in the middle
        PATH=${PATH/%":$1"/} #delete any instance at the end
        PATH=${PATH/#"$1:"/} #delete any instance at the beginning
        PATH="$1${PATH:+":$PATH"}" #prepend $1 or if $PATH is empty set to $1
    fi
}

5
2017-11-19 02:28





يجب أن يكون هناك اسم مستعار بسيط مثل هذا العنوان أدناه:

alias checkInPath="echo $PATH | tr ':' '\n' | grep -x -c "

كل ما يفعله هو تقسيم المسار على الحرف: ومقارنة كل مكون مقابل الحجة التي تمر بها. grep يتحقق من تطابق كامل للخط ، ويطبع العدد.

استخدام العينة:

$ checkInPath "/usr/local"
1
$ checkInPath "/usr/local/sbin"
1
$ checkInPath "/usr/local/sbin2"
0
$ checkInPath "/usr/local/" > /dev/null && echo "Yes" || echo "No"
No
$ checkInPath "/usr/local/bin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin2" > /dev/null && echo "Yes" || echo "No"
No

استبدل الأمر echo بـ addToPath أو بعض الاسم المستعار / الوظيفة المشابهة.


4
2017-09-11 17:26



استخدام "grep -x" ربما يكون أسرع من الحلقة التي أضعها في وظيفة bash. - Doug Harris


نرى كيفية الحفاظ على تكرار متغير المسار في csh؟ على StackOverflow لمجموعة واحدة من الإجابات على هذا السؤال.


2
2017-09-11 19:47