سؤال
لديّ نص 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 لمزيد من المعالجة.
- يمكن أيضًا تغليف الكود بأكمله في وظيفة.
- أيضا ، يمكن أيضًا تعيين موجه الإخراج إلى متغير.
- يدعم هذا النهج أيضًا تحليل
PROMPT_COMMAND
...