Frage

Ich habe ein Shell-Skript, das den gleichen Befehl in mehreren Verzeichnissen läuft ( fgit ). Für jedes Verzeichnis, würde ich es wie die aktuellen Prompt + den Befehl zu zeigen, die dort ausgeführt werden. Wie erhalte ich den String ab, entspricht den entschlüsselt (erweitert) PS1? Zum Beispiel meines Standard-PS1 ist

${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)')$

und ich möchte die resultierende schnelle username@hostname:/path$ Echo, vorzugsweise (aber nicht notwendigerweise) mit den schönen Farben. Ein flüchtiger Blick auf die Bash Handbuch gibt es noch keine definitive Antwort offenbaren und echo -e $PS1 wertet nur die Farben.

War es hilfreich?

Lösung

Ein großer Vorteil von Open-Source-Software ist, dass die Quelle ist, na ja, offen: -)

Bash selbst verfügt nicht über diese Funktionalität, aber es gibt verschiedene Tricks, die Sie eine Untergruppe (zB Substitution \u mit $USER und so weiter), um verwenden können. Dies ist jedoch eine Menge doppelter Funktionalität erfordert und sicherzustellen, dass der Code synchron gehalten wird, was auch immer bash tut in der Zukunft.

Wenn Sie möchten, bekommen alle die Macht der Prompt-Variablen (und es Ihnen nichts ausmacht die Hände schmutzig mit ein bisschen Codierung bekommen (und, wenn Sie nichts dagegen haben, warum bist du hier? )), ist es leicht genug ist, um die Schale selbst hinzuzufügen.

Wenn Sie den Code für bash herunterladen (Ich betrachte Version 4.2), gibt es eine y.tab.c Datei, die die decode_prompt_string() Funktion enthält:

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

Dies ist die Funktion, die das PSx Variablen zum Veran auswertet. Um diese Funktionalität zu ermöglichen, den Benutzern der Schale selbst zur Verfügung gestellt werden (und nicht nur verwendet, von die Shell), können Sie diese Schritte ausführen können einen internen Befehl evalps1 hinzuzufügen.

Als erstes ändern support/mkversion.sh, so dass Sie es nicht zu verwechseln mit einem „echten“ bash, und so, dass der FSF kann alles Wissen für Garantiezwecke verweigern :-) einfach eine Zeile ändern (Ich habe das -pax Bit hinzugefügt):

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

Zweitens Änderung builtins/Makefile.in eine neue Quelldatei hinzuzufügen. Dies bringt eine Reihe von Schritten.

(a) In $(srcdir)/evalps1.def bis Ende DEFSRC.

(b) In evalps1.o bis Ende OFILES.

(c) Fügen Sie die erforderlichen Abhängigkeiten:

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

Drittens, fügen Sie die builtins/evalps1.def Datei selbst, das ist der Code, der ausgeführt wird, wenn Sie den evalps1 Befehl ausführen:

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

Der größte Teil davon ist die GPL-Lizenz (da ich es von exit.def modifizierte) mit einer sehr einfachen Funktion am Ende zu erhalten und zu dekodieren PS1.

Schließlich bauen nur das, was in der obersten Ebene Verzeichnis:

./configure
make

Die bash ausführbare Datei, die kann scheint paxsh umbenannt werden, obwohl ich bezweifle es jemals so weit verbreitet wie seine Vorfahren werden: -)

Und es läuft, können Sie es in Aktion sehen:

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: ]

Wenn Sie eine der PSx Variablen setzen in die Eingabeaufforderung, Echo $PS1 einfach gibt Ihnen die Variable, während die evalps1 Befehl auswertet und gibt das Ergebnis.

Nun gewährt, Code Änderungen an bash einen internen Befehl hinzufügen können durch einige betrachtet werden Overkill aber sein, wenn Sie eine perfekte Bewertung von PS1 wollen, ist es sicherlich eine Option.

Andere Tipps

Da Bash 4.4 Sie die @P Erweiterung verwenden können:

Zuerst ich Ihren Prompt-String in einer Variablen myprompt mit read -r und zitierte hier-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

, um die Eingabeaufforderung zu drucken (wie es interpretiert werden würde, wenn es PS1 ist), verwenden Sie die Erweiterung ${myprompt@P}:

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

(In der Tat gibt es einige \001 und \002 Zeichen, die aus \[ und \], dass Sie hier nicht in, aber Sie können sie sehen, wenn Sie zu bearbeiten versuchen, diesen Beitrag, du wirst sehen, wie sie auch in Ihrem Terminal wenn Sie die Befehle eingeben).


Um loszuwerden diese zu bekommen, der Trick von Dennis Williamson auf der Bash-Mailingliste geschickt ist read -e -p zu verwenden, so dass diese Zeichen von der Readline-Bibliothek interpretiert bekommen:

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

Dies wird der Benutzer aufgefordert, mit der myprompt richtig interpretiert.

Zu diesem Beitrag beantwortet Greg Wooledge, dass Sie könnte genauso gut Streifen nur die \001 und \002 aus dem String. Dies kann wie so erreicht werden:

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

Zu diesem Beitrag beantwortet Chet Ramey, dass Sie auch die Linie mit set +o emacs +o vi ganz Bearbeitung ausschalten könnte. So wird dies tun, auch:

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

Warum gehst du nicht einfach verarbeiten die $PS1 Flucht Substitutionen selbst? Eine Reihe von Substitutionen wie diese:

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

By the way, zsh hat die Fähigkeit, schnelle Fluchten zu interpretieren.

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

oder

p=${(%%)PS1}

Ich mag die Idee von Befestigungs Bash, um es besser zu machen, und ich schätze paxdiablo ausführliche Antwort , wie Bash patchen. Ich werde einen geht irgendwann haben.

Doch ohne Bash Quellcode zu Patchen, ich habe einen Einzeiler Hack, der sowohl tragbar ist und nicht doppelte Funktionalität, weil die Problemumgehung verwendet nur Bash und seine builtins.

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

Beachten Sie, dass es etwas seltsam los mit tty ist und stdio da dies auch funktioniert:

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

Also, obwohl ich verstehe nicht, was mit dem stdio los ist hier, mein Hack funktioniert für mich auf Bash 4.2, NixOS GNU / Linux. Patchen der Bash-Quellcode auf jeden Fall eine elegantere Lösung ist, und es soll ziemlich einfach und sicher sein, jetzt zu tun, dass ich mit Nichts.

Zwei Antwort: "Reine bash" und "bash + sed"

Da dies tun, indem Sie sed ist simplier, wird die erste Antwort verwenden Lösung.

prompt Expansion, bash + sed

Es ist mein Hack:

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

Erklärung:

Ausführen bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1

zurückgeben kann so etwas wie:

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

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

Der sed Befehl wird dann

  • nehmen alle Zeilen in einem Puffer (:;$!{N;b};), als
  • ersetzen <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit durch <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • wo <everything, terminated by end-of-line> werden \1
    • und <prompt> werden \2.
Testfall:
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

Von dort sind Sie in einer Art Pseudo-interaktiv Shell (ohne Readline- Einrichtungen, aber das ist nicht Sache) ...

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$ 

(letzte Zeile drucken sowohl ubuntu in grün, @, : und $ in schwarz und Pfad (/tmp) in blau)

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

Die while Schleife ist erforderlich richtigen Umgang mit mehrzeiligen Eingabeaufforderungen, um sicherzustellen:

ersetzen 1. Zeile:

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

oder

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

Die letzte mehrzeilige druckt:

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

Sie haben möglicherweise ein kleines C-Programm zu schreiben, die den gleichen Code Bash verwendet hat (ist es eine Bibliothek zu nennen?), Dass die Eingabeaufforderung angezeigt werden, und nur das C-Programm aufrufen. Zugegeben, das ist nicht sehr portabel ist, da Sie es auf jeder Plattform kompilieren müssen werden, aber es ist eine mögliche Lösung.

Eine weitere Möglichkeit: ohne den Quellcode der Bash bearbeiten, mit script Dienstprogramm (Teil bsdutils Paket auf 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 Befehl erzeugt eine Datei angegeben und die Ausgabe auch auf stdout gezeigt. Wenn Dateiname nicht angegeben wird, erzeugt er eine Datei namens Typoskript.

Da wir in der Log-Datei in diesem Fall nicht interessiert sind, Dateiname wird als /dev/null angegeben. Statt die stdout des Script-Befehl wird zur weiteren Verarbeitung an awk übergeben.

  1. Der gesamte Code kann auch in eine Funktion eingekapselt werden.
  2. Auch die Ausgabeaufforderung kann auch zu einer Variablen zugewiesen werden.
  3. Dieser Ansatz unterstützt auch von PROMPT_COMMAND Parsen ...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top