Kann die Bash-Variablenerweiterung direkt auf der Benutzereingabe durchgeführt werden?

Kann die Bash-Variablenerweiterung direkt auf der Benutzereingabe durchgeführt werden?

Gibt es in Bash eine Möglichkeit, Benutzereingaben einzulesen und trotzdem bashdie Variablenerweiterung zuzulassen?

Ich versuche, den Benutzer mitten in einem Programm aufzufordern, einen Pfad einzugeben, aber da ~und andere Variablen nicht als Teil der readintegrierten Funktion erweitert werden, müssen Benutzer einen absoluten Pfad eingeben.

Beispiel: Wenn ein Benutzer einen Pfad eingibt in:

read -ep "input> " dirin
  [[ -d "$dirin" ]] 

Gibt „true“ zurück, wenn ein Benutzer /home/user/binaber nicht ~/binoder eingibt $HOME/bin.

Antwort1

Ein naiver Weg wäre:

eval "dirin=$dirin"

Dadurch wird die Erweiterung dirin=$dirinals Shellcode ausgewertet.

Mit dirin„containing“ ~/foowird tatsächlich Folgendes ausgewertet:

dirin=~/foo

Die Einschränkungen sind leicht zu erkennen. Mit einem dirinenthaltenen foo barwird daraus:

dirin=foo bar

Es läuft also barin dirin=fooseiner Umgebung (und Sie hätten andere Probleme mit all den Sonderzeichen der Shell).

Hier müssen Sie entscheiden, welche Erweiterungen erlaubt sind (Tilde, Befehlsersetzung, Parametererweiterung, Prozessersetzung, arithmetische Erweiterung, Dateinamenerweiterung...) und diese Ersetzungen entweder manuell vornehmen oder verwenden, evalaberFluchtalle Zeichen außer denen, die sie zulassen, was praktisch unmöglich wäre, außer durch die Implementierung eines vollständigen Shell-Syntaxparsers, es sei denn, Sie beschränken es beispielsweise auf ~foo, $VAR, ${VAR}.

Hier würde ich zshstatt bash„hat“ einen dedizierten Operator dafür verwenden:

vared -cp "input> " dirin
printf "%s\n" "${(e)dirin}"

varedist derVariableneditor, ähnlich bashwie read -e.

(e)ist ein Parametererweiterungsflag, das Erweiterungen (Parameter, Befehl, Arithmetik, aber keine Tilde) im Inhalt des Parameters durchführt.

Um die Tilde-Erweiterung zu berücksichtigen, die nur am Anfang der Zeichenfolge stattfindet, würden wir Folgendes tun:

vared -cp "input> " dirin
if [[ $dirin =~ '^(~[[:alnum:]_.-]*(/|$))(.*)' ]]; then
  eval "dirin=$match[1]\${(e)match[3]}"
else
  dirin=${(e)dirin}
fi

Um POSIX-mäßig (also bashauch mäßig) eine Tilde- und Variablen- (nicht Parameter-)Erweiterung durchzuführen, könnten Sie eine Funktion wie die folgende schreiben:

expand_var() {
  eval "_ev_var=\${$1}"
  _ev_outvar=
  _ev_v=${_ev_var%%/*}
  case $_ev_v in
    (?*[![:alnum:]._-]*) ;;
    ("~"*)
      eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
  esac

  while :; do
    case $_ev_var in
      (*'$'*)
        _ev_outvar=$_ev_outvar${_ev_var%%"$"*}
        _ev_var=${_ev_var#*"$"}
        case $_ev_var in
          ('{'*'}'*)
            _ev_v=${_ev_var%%\}*}
            _ev_v=${_ev_v#"{"}
            case $_ev_v in
              "" | [![:alpha:]_]* | *[![:alnum:]_]*) _ev_outvar=$_ev_outvar\$ ;;
              (*) eval "_ev_outvar=\$_ev_outvar\${$_ev_v}"; _ev_var=${_ev_var#*\}};;
            esac;;
          ([[:alpha:]_]*)
            _ev_v=${_ev_var%%[![:alnum:]_]*}
            eval "_ev_outvar=\$_ev_outvar\$$_ev_v"
            _ev_var=${_ev_var#"$_ev_v"};;
          (*)
            _ev_outvar=$_ev_outvar\$
        esac;;
      (*)
        _ev_outvar=$_ev_outvar$_ev_var
        break
    esac
  done
  eval "$1=\$_ev_outvar"
}

Beispiel:

$ var='~mail/$USER'
$ expand_var var;
$ printf '%s\n' "$var"
/var/mail/stephane

Als Annäherung könnten wir auch jedem Zeichen außer ~${}-_.und jedem Zahlenwert einen Backslash voranstellen, bevor wir an übergeben eval:

eval "dirin=$(
  printf '%s\n' "$dirin" |
    sed 's/[^[:alnum:]~${}_.-]/\\&/g')"

(hier vereinfacht, da es $dirinkeine Zeilenumbruchzeichen enthalten darf, wie es von kommt read)

Das würde Syntaxfehler auslösen, wenn man ${foo#bar}beispielsweise Folgendes eingibt, aber das kann zumindest nicht so viel Schaden anrichten wie ein einfaches eval.

Bearbeiten: eine funktionierende Lösung für bashund andere POSIX-Shells wäre, die Tilde und andere Erweiterungen wie in zu trennen zshund evalmit einem Here-Dokument für dieandere ErweiterungenTeil wie:

expand_var() {
  eval "_ev_var=\${$1}"
  _ev_outvar=
  _ev_v=${_ev_var%%/*}
  case $_ev_v in
    (?*[![:alnum:]._-]*) ;;
    ("~"*)
      eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
  esac
  eval "$1=\$_ev_outvar\$(cat << //unlikely//
$_ev_var
//unlikely//
)"

Das würde Tilde-, Parameter-, Arithmetik- und Befehlserweiterungen wie zshoben ermöglichen. }

verwandte Informationen