944 Stimmen

Zuverlässiger Weg für ein Bash-Skript, den vollständigen Pfad zu sich selbst zu erhalten

Ich habe ein Bash-Skript, das seinen vollständigen Pfad kennen muss. Ich versuche, einen weitgehend kompatiblen Weg zu finden, um das zu tun, ohne mit relativen oder komisch aussehenden Pfaden zu enden. Ich brauche nur Bash zu unterstützen, nicht sh, csh, etc.

Was ich bis jetzt herausgefunden habe:

  1. Die akzeptierte Antwort auf Abrufen des Quellverzeichnisses eines Bash-Skripts aus Adressen, die den Pfad des Skripts über dirname $0 was in Ordnung ist, aber das kann zu einem relativ Pfad (wie . ), was ein Problem darstellt, wenn Sie Verzeichnisse im Skript ändern und der Pfad weiterhin auf das Skriptverzeichnis verweisen soll. Trotzdem, dirname wird ein Teil des Puzzles sein.

  2. Die akzeptierte Antwort auf Absoluter Pfad eines Bash-Skripts unter OS X (OS X-spezifisch, aber die Antwort funktioniert trotzdem) gibt eine Funktion an, die prüft, ob $0 relativ aussieht und, falls dies der Fall ist, vorangestellt wird $PWD dazu. Aber das Ergebnis kann immer noch relative Bits enthalten (obwohl es insgesamt absolut ist) - zum Beispiel, wenn das Skript t im Verzeichnis /usr/bin und Sie sind in /usr und Sie geben ein bin/../bin/t um es auszuführen (ja, das ist verworren), landet man bei /usr/bin/../bin als Verzeichnispfad für das Skript. Welche Werke ...aber...

  3. Le site readlink Lösung auf dieser Seite die wie folgt aussieht:

    # Absolute path to this script. /home/user/bin/foo.sh
    SCRIPT=$(readlink -f $0)
    # Absolute path this script is in. /home/user/bin
    SCRIPTPATH=`dirname $SCRIPT`

    Aber readlink ist nicht POSIX und die Lösung beruht offenbar auf GNUs readlink wo BSDs aus irgendeinem Grund nicht funktionieren (ich habe keinen Zugang zu einem BSD-ähnlichen System, um das zu überprüfen).

Es gibt also verschiedene Möglichkeiten, die aber alle ihre Tücken haben.

Was wäre ein besserer Weg? Wobei "besser" bedeutet:

  • Das gibt mir den absoluten Pfad.
  • Entfernt störende Teile, selbst wenn sie auf komplizierte Weise aufgerufen werden (siehe Kommentar zu Nr. 2 oben). (Z.B. wird der Pfad zumindest mäßig kanonisiert.)
  • Bezieht sich nur auf Bash-ismen oder Dinge, die mit ziemlicher Sicherheit in den meisten gängigen Varianten von *nix-Systemen (GNU/Linux, BSD und BSD-ähnlichen Systemen wie OS X usw.) enthalten sind.
  • Vermeidet, wenn möglich, den Aufruf externer Programme (z.B. bevorzugt Bash built-ins).
  • ( Aktualisiert danke für die Vorwarnung, welche ) Es muss keine Symlinks auflösen (mir wäre es sogar lieber, wenn es sie in Ruhe ließe, aber das ist keine Voraussetzung).

0voto

wich Punkte 15933

Nur so zum Spaß habe ich ein bisschen an einem Skript herumgeschraubt, das Dinge rein textuell, rein in Bash erledigt. Ich hoffe, ich habe alle Randfälle erwischt.

Beachten Sie, dass die ${var//pat/repl} die ich in der anderen Antwort erwähnt habe, funktioniert nicht, da man nicht dafür sorgen kann, dass nur die kürzestmögliche Übereinstimmung ersetzt wird, was ein Problem beim Ersetzen von /foo/../ wie z.B. /*/../ nimmt alles, was davor steht, und nicht nur einen einzigen Eintrag. Und da diese Muster nicht wirklich Regexe sind, weiß ich nicht, wie das funktionieren soll. Hier ist also die hübsch verschlungene Lösung, die ich mir ausgedacht habe, viel Spaß ;)

Übrigens, lassen Sie mich wissen, wenn Sie irgendwelche unbehandelten Randfälle finden.

#!/bin/bash

canonicalize_path() {
  local path="$1"
  OIFS="$IFS"
  IFS=$'/'
  read -a parts < <(echo "$path")
  IFS="$OIFS"

  local i=${#parts[@]}
  local j=0
  local back=0
  local -a rev_canon
  while (($i > 0)); do
    ((i--))
    case "${parts[$i]}" in
      ""|.) ;;
      ..) ((back++));;
      *) if (($back > 0)); then
           ((back--))
         else
           rev_canon[j]="${parts[$i]}"
           ((j++))
         fi;;
    esac
  done
  while (($j > 0)); do
    ((j--))
    echo -n "/${rev_canon[$j]}"
  done
  echo
}

canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"

-1voto

Meow Punkte 3805

Eine weitere Möglichkeit, dies zu tun:

shopt -s extglob

selfpath=$0
selfdir=${selfpath%%+([!/])}

while [[ -L "$selfpath" ]];do
  selfpath=$(readlink "$selfpath")
  if [[ ! "$selfpath" =~ ^/ ]];then
    selfpath=${selfdir}${selfpath}
  fi
  selfdir=${selfpath%%+([!/])}
done

echo $selfpath $selfdir

-4voto

Elendurwen Punkte 882

Einfacher ausgedrückt: Das ist es, was für mich funktioniert:

MY_DIR=`dirname $0`
source $MY_DIR/_inc_db.sh

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X