سؤال

لديّ نص Shell الذي يدير نفس الأمر في عدة أدلة (fgit). لكل دليل ، أود أن يعرض الموجه الحالي + الأمر الذي سيتم تشغيله هناك. كيف أحصل على السلسلة التي تتوافق مع فك تشفير (موسع)PS1؟ على سبيل المثال ، PS1 الافتراضي الخاص بي

${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$

وأود أن أردد المطالبة الناتجة username@hostname:/path$, ، ويفضل (ولكن ليس بالضرورة) مع الألوان الجميلة. لم تكشف نظرة سريعة على دليل باش عن أي إجابة محددة ، و echo -e $PS1 يقيم فقط الألوان.

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

المحلول

ميزة واحدة رائعة للبرنامج المفتوح المصدر هو أن المصدر مفتوح جيدًا :-)

لا توفر Bash نفسها هذه الوظيفة ولكن هناك حيل مختلفة يمكنك استخدامها لتوفير مجموعة فرعية (مثل الاستبدال \u مع $USER وهلم جرا). ومع ذلك ، فإن هذا يتطلب الكثير من تكرار الوظائف وضمان الحفاظ على الكود متزامن مع أي شيء bash لا في المستقبل.

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

إذا قمت بتنزيل الرمز لـ bash (أنا أبحث في الإصدار 4.2) ، هناك أ y.tab.c الملف الذي يحتوي على decode_prompt_string() وظيفة:

char *decode_prompt_string (string) char *string; { ... }

هذه هي الوظيفة التي تقيم PSx المتغيرات للرضا. من أجل السماح بتوفير هذه الوظيفة لمستخدمي الصدفة نفسها (بدلاً من استخدامها للتو بواسطة القشرة) ، يمكنك متابعة هذه الخطوات لإضافة أمر داخلي evalps1.

أولا ، التغيير support/mkversion.sh حتى لا تخلط بينها وبين "حقيقي" bash, ، وبالتالي يمكن لـ FSF أن تنكر جميع المعرفة لأغراض الضمان :-) ببساطة تغيير سطر واحد (أضفت -pax قليل):

echo "#define DISTVERSION \"${float_dist}-pax\""

ثانياً ، التغيير builtins/Makefile.in لإضافة ملف مصدر جديد. هذا يستلزم عدد من الخطوات.

(أ) إضافة $(srcdir)/evalps1.def حتى نهاية DEFSRC.

(ب) إضافة evalps1.o حتى نهاية OFILES.

(ج) إضافة التبعيات المطلوبة:

evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \
           $(topdir)/bashintl.h $(topdir)/shell.h common.h

ثالثًا ، أضف builtins/evalps1.def الملف نفسه ، هذا هو الرمز الذي يتم تنفيذه عند تشغيل evalps1 يأمر:

This file is evalps1.def, from which is created evalps1.c.
It implements the builtin "evalps1" in Bash.

Copyright (C) 1987-2009 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Bash.  If not, see <http://www.gnu.org/licenses/>.

$PRODUCES evalps1.c

$BUILTIN evalps1
$FUNCTION evalps1_builtin
$SHORT_DOC evalps1
Outputs the fully interpreted PS1 prompt.

Outputs the PS1 prompt, fully evaluated, for whatever nefarious purposes
you require.
$END

#include <config.h>
#include "../bashtypes.h"
#include <stdio.h>
#include "../bashintl.h"
#include "../shell.h"
#include "common.h"

int
evalps1_builtin (list)
     WORD_LIST *list;
{
  char *ps1 = get_string_value ("PS1");
  if (ps1 != 0)
  {
    ps1 = decode_prompt_string (ps1);
    if (ps1 != 0)
    {
      printf ("%s", ps1);
    }
  }
  return 0;
}

الجزء الأكبر من ذلك هو ترخيص GPL (منذ أن قمت بتعديله من exit.def) مع وظيفة بسيطة للغاية في النهاية للحصول على وفك الشفرة PS1.

أخيرًا ، فقط قم ببناء الشيء في الدليل الأعلى:

./configure
make

ال bash يمكن إعادة تسمية القابلة للتنفيذ paxsh, ، على الرغم من أنني أشك في أنها ستصبح سائدة مثل سلفها :-)

وتشغيله ، يمكنك رؤيته في العمل:

pax> mv bash paxsh

pax> ./paxsh --version
GNU bash, version 4.2-pax.0(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

pax> ./paxsh

pax> echo $BASH_VERSION
4.2-pax.0(1)-release

pax> echo "[$PS1]"
[pax> ]

pax> echo "[$(evalps1)]"
[pax> ]

pax> PS1="\h: "

paxbox01: echo "[$PS1]"
[\h: ]

paxbox01: echo "[$(evalps1)]"
[paxbox01: ]

عندما تضع واحدة من PSx المتغيرات في المطالبة ، صدى $PS1 ببساطة يمنحك المتغير ، بينما evalps1 يقوم الأمر بتقييمه ويخرج النتيجة.

الآن ، منحت ، إجراء تغييرات في الكود bash لإضافة أمر داخلي ، قد يكون البعض مبالغًا فيه ، ولكن إذا كنت تريد تقييمًا مثاليًا لـ PS1, ، إنه بالتأكيد خيار.

نصائح أخرى

منذ Bash 4.4 يمكنك استخدام @P توسع:

أولا وضعت سلسلة المطالبة الخاصة بك في متغير myprompt استخدام read -r وأقتبس هنا-DOC:

read -r myprompt <<'EOF'
${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$ 
EOF

لطباعة المطالبة (كما سيتم تفسيرها إذا كانت كذلك PS1) ، استخدم التوسع ${myprompt@P}:

$ printf '%s\n' "${myprompt@P}"
gniourf@rainbow:~$
$

(في الواقع هناك بعض \001 و \002 شخصيات قادمة من \[ و \] لا يمكنك رؤيتها هنا ، ولكن يمكنك رؤيتها إذا حاولت تحرير هذا المنشور ؛ ستراهم أيضًا في المحطة الخاصة بك إذا قمت بكتابة الأوامر).


للتخلص من هذه ، فإن الخدعة التي أرسلها دينيس ويليامسون في قائمة البشر البريدية هي الاستخدام read -e -p بحيث يتم تفسير هذه الشخصيات بواسطة مكتبة القراءة:

read -e -p "${myprompt@P}"

هذا سيؤدي إلى دفع المستخدم ، مع myprompt تم تفسيرها بشكل صحيح.

إلى هذا المنشور ، أجاب جريج وولدج أنك قد تجرد فقط \001 و \002 من السلسلة. يمكن تحقيق ذلك مثل:

myprompt=${myprompt@P}
printf '%s\n' "${myprompt//[$'\001'$'\002']}"

إلى هذا المنشور ، أجاب Chet Ramey أنه يمكنك أيضًا إيقاف تحرير الخط بالكامل set +o emacs +o vi. لذلك هذا سوف يفعل أيضا:

( set +o emacs +o vi; printf '%s\n' "${myprompt@P}" )

لماذا لا تعالج فقط $PS1 الهروب من بدائل نفسك؟ سلسلة من البدائل مثل هذه:

p="${PS1//\\u/$USER}"; p="${p//\\h/$HOSTNAME}"

بالمناسبة ، لدى ZSH القدرة على تفسير الهروب السريع.

print -P '%n@%m %d'

أو

p=${(%%)PS1}

يعجبني فكرة إصلاح باش لجعلها أفضل ، وأنا أقدر Paxdiablo's إجابة مطول على كيفية تصحيح باش. سأذهب في وقت ما.

ومع ذلك ، بدون ترقيع رمز مصدر Bash ، لدي اختراق واحد محمول ولا يكرر الوظائف ، لأن الحل البديل يستخدم فقط Bash و Bruimins.

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1)"; echo "'${x%exit}'"

لاحظ أن هناك شيئًا غريبًا يحدث معه tty'رمل stdio النظر لأن هذا يعمل أيضًا:

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1 > /dev/null)"; echo "'${x%exit}'"

لذلك على الرغم من أنني لا أفهم ما يحدث مع stdio هنا ، يعمل اختراقي على Bash 4.2 ، Nixos GNU/Linux. يعد تصحيح رمز مصدر Bash حلًا أكثر أناقة ، ويجب أن يكون من السهل وآمن القيام به الآن بعد أن أستخدم NIX.

إجابة اثنين: "باش نقي" و "باش + سيد"

كما تفعل هذا باستخدام sed هو أبسط ، سيتم استخدام الإجابة الأولى .

انظر أدناه ل نقي المحلول.

التوسع الفوري ، bash + sed

هناك الاختراق الخاص بي:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
              sed ':;$!{N;b};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"

تفسير:

ادارة bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1

قد تعيد شيئًا مثل:

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@ubuntu:~$ 
ubuntu@ubuntu:~$ exit

ال sed سوف الأمر بعد ذلك

  • خذ جميع الخطوط في مخزن مؤقت واحد (:;$!{N;b};)، من
  • يحل محل <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit بواسطة <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • أين <everything, terminated by end-of-line> أصبح \1
    • و <prompt> أصبح \2.
حالة اختبار:
while ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
          sed ':;$!{N;b};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"
    read -rp "$ExpPS1" && [ "$REPLY" != exit ] ;do
    eval "$REPLY"
  done

من هناك ، أنت في نوع من القشرة التفاعلية الزائفة (بدون مرافق للقراءة ، لكن هذا لا يهم) ...

ubuntu@ubuntu:~$ cd /tmp
ubuntu@ubuntu:/tmp$ PS1="${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$ "
ubuntu@ubuntu:/tmp$ 

(الخط الأخير طباعة كلاهما ubuntu بالأخضر، @, : و $ باللون الأسود والمسار (/tmp) باللون الأزرق)

ubuntu@ubuntu:/tmp$ exit
ubuntu@ubuntu:/tmp$ od -A n -t c <<< $ExpPS1 
 033   [   1   ;   3   2   m   u   b   u   n   t   u 033   [   0
   m   @ 033   [   1   ;   3   2   m   u   b   u   n   t   u 033
   [   0   m   : 033   [   1   ;   3   4   m   ~ 033   [   0   m
   $  \n

نقي

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
ExpPS1_W="${ExpPS1%exit}"
ExpPS1="${ExpPS1_W##*$'\n'}"
ExpPS1_L=${ExpPS1_W%$'\n'$ExpPS1}
while [ "${ExpPS1_W%$'\n'$ExpPS1}" = "$ExpPS1_W" ] ||
      [ "${ExpPS1_L%$'\n'$ExpPS1}" = "$ExpPS1_L" ] ;do
    ExpPS1_P="${ExpPS1_L##*$'\n'}"
    ExpPS1_L=${ExpPS1_L%$'\n'$ExpPS1_P}
    ExpPS1="$ExpPS1_P"$'\n'"$ExpPS1"
  done

ال while مطلوب حلقة لضمان التعامل الصحيح لمطالبات متعددة:

استبدل السطر الأول عن طريق:

ExpPS1="$(bash --rcfile <(echo "PS1='${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$ '") -i <<<'' 2>&1)"

أو

ExpPS1="$(bash --rcfile <(echo "PS1='Test string\n$(date)\n$PS1'") -i <<<'' 2>&1)";

الاخير متعددة سوف يطبع:

echo "$ExpPS1"
Test string
Tue May 10 11:04:54 UTC 2016
ubuntu@ubuntu:~$ 

od -A n -t c  <<<${ExpPS1}
   T   e   s   t       s   t   r   i   n   g  \r       T   u   e
       M   a   y       1   0       1   1   :   0   4   :   5   4
       U   T   C       2   0   1   6  \r     033   ]   0   ;   u
   b   u   n   t   u   @   u   b   u   n   t   u   :       ~  \a
   u   b   u   n   t   u   @   u   b   u   n   t   u   :   ~   $
  \n

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

احتمال واحد آخر: بدون تحرير رمز مصدر باش ، باستخدام script فائدة (جزء من bsdutils حزمة على Ubuntu):

$ TEST_PS1="\e[31;1m\u@\h:\n\e[0;1m\$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk 'NR==2' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
echo -n $RANDOM_STRING
echo -n $RANDOM_STRING
exit
EOF
<prints the prompt properly here>

script يقوم الأمر بإنشاء ملف محدد ويتم عرض الإخراج أيضًا على STDOUT. إذا تم حذف اسم الملف ، فإنه ينشئ ملفًا يسمى TypeScript.

نظرًا لأننا لسنا مهتمين بملف السجل في هذه الحالة ، يتم تحديد اسم الملف على أنه /dev/null. بدلاً من ذلك ، يتم تمرير stdout لأمر البرنامج النصي إلى AWK لمزيد من المعالجة.

  1. يمكن أيضًا تغليف الكود بأكمله في وظيفة.
  2. أيضا ، يمكن أيضًا تعيين موجه الإخراج إلى متغير.
  3. يدعم هذا النهج أيضًا تحليل PROMPT_COMMAND...
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top