Frage

Ich habe eine Variable in meinem Bash-Skript, dessen Wert so etwas wie folgt aus:

~/a/b/c

Beachten Sie, dass es nicht ausgedehnte Tilde ist. Wenn ich ls auf dieser Variable -lt (es $ VAR nennen), ich habe kein solches Verzeichnis. Ich möchte lassen bash interpretieren / erweitert diese Variable, ohne diese auszuführen. Mit anderen Worten, ich möchte bash zu laufen eval aber nicht den ausgewerteten Befehl ausführen. Ist dies möglich, in der Bash?

Wie habe ich diese Unter ohne Expansion in mein Skript zu übergeben? Ich habe die Argumentation in es mit doppelten Anführungszeichen umgeben.

Versuchen Sie diesen Befehl, um zu sehen, was ich meine:

ls -lt "~"

Das ist genau die Situation, die ich in bin. Ich möchte, dass die Tilde erweitert werden. Mit anderen Worten, was soll ich ersetzen Magie mit auf diese beiden Befehle identisch zu machen:

ls -lt ~/abc/def/ghi

und

ls -lt $(magic "~/abc/def/ghi")

Beachten Sie, dass ~ / abc / def / ghi kann oder nicht vorhanden sind.

War es hilfreich?

Lösung

Aufgrund der Natur von Stackoverflow, kann ich nicht nur diese Antwort nicht akzeptiert, aber in den dazwischenliegenden 5 Jahren seit ich gepostet gibt es weit bessere Antworten als meine zugegebenermaßen rudimentäre und ziemlich schlechte Antwort (ich war jung, tötet mich nicht).

Die anderen Lösungen in diesem Thread sind sicherer und bessere Lösungen. Vorzugsweise würde ich mit einem dieser beiden gehen:


Original Antwort für historische Zwecke (aber bitte verwenden Sie diese nicht)

Wenn ich mich nicht irre, "~" wird nicht durch einen Bash-Skript auf diese Weise erweitert werden, weil sie als Zeichenkette "~" behandelt wird. Sie können Erweiterung über eval wie dies erzwingen.

#!/bin/bash

homedir=~
eval homedir=$homedir
echo $homedir # prints home path

Alternativ nur ${HOME} verwenden, wenn Sie den Home-Verzeichnis des Benutzers mögen.

Andere Tipps

Wenn die Variable var ist durch den Benutzer eingegeben, eval sollte nicht verwendet werden, um die Tilde zu erweitern mit

eval var=$var  # Do not use this!

Der Grund dafür ist. Der Benutzer könnte durch Zufall (oder nach Verwendungszweck) Typ zum Beispiel var="$(rm -rf $HOME/)" mit möglichen katastrophalen Folgen

Ein besseres (und sicherer) Art und Weise ist Bash Parameter Erweiterung zu verwenden:

var="${var/#\~/$HOME}"

Plagarizing mich von einer vorherigen Antwort , dies ohne die Sicherheit robust tun Risiken mit eval verbunden:

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read -r _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

... als ...

path=$(expandPath '~/hello')

Alternativ kann ein einfacher Ansatz, Anwendungen eval vorsichtig:

expandPath() {
  case $1 in
    ~[+-]*)
      local content content_q
      printf -v content_q '%q' "${1:2}"
      eval "content=${1:0:2}${content_q}"
      printf '%s\n' "$content"
      ;;
    ~*)
      local content content_q
      printf -v content_q '%q' "${1:1}"
      eval "content=~${content_q}"
      printf '%s\n' "$content"
      ;;
    *)
      printf '%s\n' "$1"
      ;;
  esac
}

Ein sicherer Weg, eval Gebrauch ist "$(printf "~/%q" "$dangerous_path")". Beachten Sie, dass bash spezifisch ist.

#!/bin/bash

relativepath=a/b/c
eval homedir="$(printf "~/%q" "$relativepath")"
echo $homedir # prints home path

finden Sie unter Details href="https://stackoverflow.com/questions/2069467/">

Auch beachten Sie, dass unter zsh dies würde als so einfach sein wie echo ${~dangerous_path}

Wie wäre es damit:

path=`realpath "$1"`

Oder:

path=`readlink -f "$1"`

Die Erweiterung (kein Wortspiel beabsichtigt) auf birryree sind und halloleo die Antworten: Der allgemeine Ansatz ist eval zu verwenden, aber es kommt mit einigen wichtigen Einschränkungen, nämlich Räumen und Ausgabeumleitung (>) in den Variablen. Die folgende scheint Arbeit für mich:

mypath="$1"

if [ -e "`eval echo ${mypath//>}`" ]; then
    echo "FOUND $mypath"
else
    echo "$mypath NOT FOUND"
fi

Versuchen Sie es mit jedem der folgenden Argumente:

'~'
'~/existing_file'
'~/existing file with spaces'
'~/nonexistant_file'
'~/nonexistant file with spaces'
'~/string containing > redirection'
'~/string containing > redirection > again and >> again'

Erklärung

  • Die ${mypath//>} abstreift > Zeichen, die eine Datei während der eval verprügeln konnte.
  • Die eval echo ... ist, was tut die tatsächliche Tilde-Erweiterung
  • Die doppelten Anführungszeichen um das -e Argument sind für die Unterstützung von Dateinamen mit Leerzeichen.

Vielleicht gibt es eine elegantere Lösung, aber das ist, was ich in der Lage war, mit zu kommen.

Ich glaube, das ist das, was Sie suchen

magic() { # returns unexpanded tilde express on invalid user
    local _safe_path; printf -v _safe_path "%q" "$1"
    eval "ln -sf ${_safe_path#\\} /tmp/realpath.$$"
    readlink /tmp/realpath.$$
    rm -f /tmp/realpath.$$
}

Beispiel Nutzung:

$ magic ~nobody/would/look/here
/var/empty/would/look/here

$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand

Hier ist meine Lösung:

#!/bin/bash


expandTilde()
{
    local tilde_re='^(~[A-Za-z0-9_.-]*)(.*)'
    local path="$*"
    local pathSuffix=

    if [[ $path =~ $tilde_re ]]
    then
        # only use eval on the ~username portion !
        path=$(eval echo ${BASH_REMATCH[1]})
        pathSuffix=${BASH_REMATCH[2]}
    fi

    echo "${path}${pathSuffix}"
}



result=$(expandTilde "$1")

echo "Result = $result"

Just Verwendung eval richtig. Mit Validierung

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"

Hier ist die POSIX-Funktion Äquivalent von Håkon Hægland der Bash Antwort

expand_tilde() {
    tilde_less="${1#\~/}"
    [ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
    printf '%s' "$tilde_less"
}

2017.12.10 bearbeiten. Add '%s' pro @CharlesDuffy in den Kommentaren

Just erweitern birryree 's Antwort für Pfade mit Leerzeichen: Sie können den eval Befehl verwenden können, wie, weil es seperates Auswertung durch Leerzeichen. Eine Lösung ist Räume vorübergehend für den eval Befehl zu ersetzen:

mypath="~/a/b/c/Something With Spaces"
expandedpath=${mypath// /_spc_}    # replace spaces 
eval expandedpath=${expandedpath}  # put spaces back
expandedpath=${expandedpath//_spc_/ }
echo "$expandedpath"    # prints e.g. /Users/fred/a/b/c/Something With Spaces"
ls -lt "$expandedpath"  # outputs dir content

Dieses Beispiel beruht natürlich auf der Annahme, dass mypath nie die char-Sequenz "_spc_" enthält.

Sie können diese leichter finden in Python zu tun.

(1) aus der Unix-Kommandozeile:

python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred

Ergebnisse in:

/Users/someone/fred

(2) Innerhalb eines Bash-Skript als eine einmalige - speichern als test.sh:

#!/usr/bin/env bash

thepath=$(python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' $1)

echo $thepath

Beim Laufen bash ./test.sh ergibt:

/Users/someone/fred

(3) Als Versorger - speichern als expanduser irgendwo auf dem Weg, mit Berechtigungen ausführen:

#!/usr/bin/env python

import sys
import os

print os.path.expanduser(sys.argv[1])

Dies könnte dann in der Befehlszeile verwendet werden:

expanduser ~/fred

oder in einem Skript:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

Einfachstes :. Ersetzen 'Magie' mit 'eval echo'

$ eval echo "~"
/whatever/the/f/the/home/directory/is

Problem: Sie werden in Fragen mit anderen Variablen laufen, weil eval ist böse. Zum Beispiel:

$ # home is /Users/Hacker$(s)
$ s="echo SCARY COMMAND"
$ eval echo $(eval echo "~")
/Users/HackerSCARY COMMAND

Beachten Sie, dass die Frage der Injektion nicht auf der ersten Expansion geschieht. Also, wenn Sie sind einfach magic mit eval echo ersetzen, sollten Sie in Ordnung sein. Aber wenn Sie echo $(eval echo ~) tun, das wäre der Injektion anfällig sein.

Und falls Sie tun eval echo ~ statt eval echo "~", das wäre als zweimal erweitert zählen und daher wäre Injektion möglich rechts weg sein.

I have done this with variable parameter substitution after reading in the path using read -e (among others). So the user can tab-complete the path, and if the user enters a ~ path it gets sorted.

read -rep "Enter a path:  " -i "${testpath}" testpath 
testpath="${testpath/#~/${HOME}}" 
ls -al "${testpath}" 

The added benefit is that if there is no tilde nothing happens to the variable, and if there is a tilde but not in the first position it is also ignored.

(I include the -i for read since I use this in a loop so the user can fix the path if there is a problem.)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top