CD zu einer (übergebenen) Variable im Bash-Skript ist nicht möglich

CD zu einer (übergebenen) Variable im Bash-Skript ist nicht möglich

Ich verwende Bash, um einige Informationen von Audacious an Conky zur Anzeige zu übermitteln (einschließlich Albumcover). Ich erhalte den Pfad zum Verzeichnis der Musikdatei von Audacious und versuche dann, per cd in dieses Verzeichnis zu wechseln, um folder.jpg zu finden. Wenn es gefunden wird, bereite ich es zur Anzeige vor. Leider kann ich mich nicht auf „wohlgeformte“ Pfadnamen verlassen. Keiner von ihnen hat ein Problem vom Terminal aus, aber die Auswertungen, die Bash macht … Es versinkt in doppelten Leerzeichen ( ) ' / und wahrscheinlich anderen, obwohl das aktuelle Setup damit klarzukommen scheint – ok. Hier ist die relevante Funktion:

GetArt ()
{
    file_path=`audtool --current-song-tuple-data file-path` # get the path to the song
    file_path=$(eval echo "${file_path}")                   # pre-expand to full path
    cd "${file_path}"
    if [[ ! -e "folder.jpg" ]];                             # if no art work found
    then
        cp ~/Work/vinyl.png /tmp/cover.png              # put in placeholder
    else
        convert "${file_path}""/folder.jpg" -resize 120x120 /tmp/cover.png # ready for showing
    fi
}

Irgendwelche Ideen, oder wäre es einfacher, die Drahtbürste rauszuholen und den Rost von meinem C-Compiler zu entfernen?

Ich habe es mit doppelten Anführungszeichen, einfachen Anführungszeichen, Backticks und sogar einer Konstruktion wie dieser versucht:

code=$code \"\$filename\""

aber bisher scheint nichts richtig zu funktionieren. Glücklicherweise schlägt es ziemlich fehl, weil stattdessen nur das Ersatzbild „Kann Art nicht finden“ angezeigt wird, aber manchmal rülpst alles über stderr, bis zum nächsten Song – oder Album.

Antwort1

Sie können file_path="${file_path/#~/$HOME}"mit erweiternNurDie ~.

Soweit ich sagen kann,Tilde-Erweiterungist die einzige Art von Erweiterung, die auf die Ausgabe von angewendet werden muss audtool --current-song-tuple-data file-path. Darüber hinaus tritt nur ein spezieller Fall der Tilde-Notation auf: Wenn der von Audacious verwendete Pfad mit dem Home-Verzeichnis des Benutzers beginnt, der Audacious ausführt, wird dieser Teil des Pfads ~in der Ausgabe von durch ein ersetzt audtool. Da dieaudaciousUndaudtoolDie Manpages geben dazu keine Auskunft. Ich habe versucht, audtoolAusgabepfade mit der ~usernameForm einer Tilde-Erweiterung zu erhalten, aber glücklicherweise scheint nicht einmal das zu passieren. Ich sage „glücklicherweise“, weil das bedeutet, dass die Situation ziemlich einfach ist.

Da die einzige Transformation, die an der Ausgabe dieses Befehls vorgenommen werden muss, audtooldarin besteht, ein führendes Zeichen ~durch den Pfad des Home-Verzeichnisses des Benutzers zu ersetzen, können Sie einfach Code in Ihr Skript schreiben, der diese Erweiterung selbst durchführt und den Pfad ansonsten unverändert lässt. Sie haben festgestellt, dass Dateinamen von Audiodateien, die Sie möglicherweise abspielen, Zeichen enthalten können, die von der Shell speziell behandelt werden, undWenn Sie Dateien abspielen, die von anderen Personen benannt wurden,kann sogar zu einer Sicherheitslücke führenin Ihrem Skript. Indem Sie die Shell keinen beliebigen, nicht vertrauenswürdigen Text ausführen – oder gar erweitern – lassen, vermeiden Sie dieses Problem vollständig.

Es gibt mehrere Möglichkeiten, ~sich zu erweitern. Ich schlage vor:

file_path="$(audtool --current-song-tuple-data file-path)"  # might need tilde expansion
file_path="${file_path/#~/$HOME}"  # do the tilde expansion, if needed

Dadurch wird die eine Transformation, die möglicherweise erforderlich ist, manuell durchgeführt. Insbesondere wenn die Ausgabe von audtoolmit einem beginnt ~, wird es durch das Home-Verzeichnis des Benutzers ersetzt, das durch Überprüfen des Wertes der HOMEVariablen ermittelt wird. Wenn die Ausgabe nicht mit einem beginnt ~, wird keine Erweiterung durchgeführt.

${file_path/#~/$HOME}Beachten Sie , dass dieser Ansatznichtwäre richtig, wenn es als Versuch verwendet würde, alle Formen der Tilde-Erweiterung zu simulieren, da es in der ~usernameForm (und in einigen der anderen, weniger bekannten Formen) falsche Ersetzungen vornehmen würde. Solange Pfade in der Ausgabe jedoch audtoolnur die Tilde-Notation verwenden, wenn einfach das Home-Verzeichnis des aktuellen Benutzers bezeichnet wird – was meines Erachtens der Fall ist –, dann ist dieser AnsatzIstfür diese Pfade angemessen und richtig.

So funktioniert es:

  • Allgemein,Bash wird erweitert ${parameter/pattern/string}in den Wert von parameter, aber mitder Teil, der passtpatternersetzt durch string. Wenn kein Teil dem Muster entspricht, wird der genaue Wert von parameterverwendet.
  • Wenn patternmit einem führenden geschrieben wird #, kann es nur ab dem Anfang des Wertes von abgeglichen werden parameter. (Es gibt auch andere Zeichen #, die an dieser Stelle sinnvoll sind: ein %würde erfordern, dass das Muster am Anfang abgeglichen wird.Ende, und a /würde dazu führen, dass das Muster so oft abgeglichen und ersetzt wird, wie es vorkommt, und nicht höchstens einmal.)
  • ~hat in einem Muster keine besondere Bedeutung und wird daher wörtlich als ein abzugleichendes und zu ersetzendes Zeichen behandelt.

SehenParametererweiterungfür Details.

Beachten Sie, dass der Code, den ich geschrieben habe, nichts tut, um den Fall einer audtoolleeren Ausgabe zu behandeln, wie es passiert, wenn gerade kein Lied abgespielt wird (oder, wieSimon Sudler erwähnte, wenn ein Fehler auftritt). Es wird jedoch bei einer leeren Ausgabe nicht unterbrochen und behindert auch nicht die nachfolgende Verarbeitung. Wenn Sie diesen Fall also abdecken möchten, können Sie das immer noch tun.

Abschließend möchte ich erwähnen, dass ich zwei Unterschiede übergangen habe, die meiner Meinung nach für Ihren Anwendungsfall unwichtig sind:

  1. Da audtooldie Pfade von aus Audacious stammen, müssten Sie, wenn Sie die bizarre Situation versuchen würden, Audacious als ein Benutzer und als ein anderer Benutzer auszuführen audtool– und Sie D-Bus irgendwie so konfiguriert hätten, dass dies funktioniert –, darauf achten, ~auf das Home-Verzeichnis eines anderen Benutzers zu verweisen als des Benutzers, der das Skript ausführt.
  2. Technisch gesehen ist die Tilde-Erweiterung für den aktuellen Benutzer (die in einer Shell mit ~allein oder unmittelbar gefolgt von erfolgt /) nichtganzentspricht "$HOME". Shells können versuchen, den seltsamen Fall zu behandeln, in dem HOMEnicht gesetzt ist. Bash versucht dies zu behandeln und trotzdem eine korrekte Erweiterung zu erzeugen. Ich bezweifle jedoch, dass Sie sich für diesen Zweck für die Unterscheidung interessieren. SieheTilde-Erweiterungfür Details.

Antwort2

Die Neuzuweisung der file_pathVariablen ist nicht notwendig. Es können zwei Anzeichen auftreten, die das Skript beschädigen könnten:

  1. Die Variable ist leer, da sie audtooleinen Fehler zurückgibt
  2. Gibt audtoolsein Verzeichnis zurück, das Leerzeichen enthält.
  3. audtoolIncludes ~für das Home-Verzeichnis

Hier ein Vorschlag zur Lösung dieser Probleme:

GetArt ()
{
    file_path="$(audtool --current-song-tuple-data file-path)"
    file_path="$(readlink -f "$(bash -c "echo $file_path")")"
    if [[ ! -d $file_path ]]; then
        echo "error: $file_path does not exists"
        exit 1
    fi
    folder_jpg="$file_path/folder.jpg"
    if [[ ! -e "$folder_jpg" ]]; then
        cp ~/Work/vinyl.png /tmp/cover.png
    else
        convert "$folder_jpg" -resize 120x120 /tmp/cover.png
    fi
}

Dies "$(command)"behandelt die Leerzeichen im Ordnernamen (falls welche vorhanden sind). Verwenden Sie es nicht cdinnerhalb eines Skripts, wenn Sie es nicht benötigen. Es ist immer eine gute Idee zu prüfen, ob die Rückgabezeichenfolge ein Verzeichnis ist. Das Arbeiten mit dem absoluten Pfad ist in den meisten Fällen besser und verhindert die Ausführung von Befehlen im falschen Ordner.

Hinweise zur ~Erweiterung in der Bash:

Die Erweiterung von ~wird von der Bash gehandhabt und readlink -fhilft hier daher nicht. Daher ~muss die Variable, die das enthält, ohne Anführungszeichen ausgeführt werden, um erweitert zu werden (ich habe das Skript aktualisiert). Dies kann natürlich in einer einzigen Zeile gehandhabt werden ...

# expansion from current bash
$ set -x; readlink -f ~; set +x
+ readlink -f /home/user
/home/user
+ set +x

# expansion will not work, with quoted ~
$ set -x; readlink -f '~'; set +x
+ readlink -f '~'
/home/user/~
+ set +x

# expansion from un-quoted ~ with sub-shell
$ set -x; readlink -f "$(bash -c "echo ~")"; set +x
++ bash -c 'echo ~'
+ readlink -f /home/user
/home/user
+ set +x

verwandte Informationen