blog · git · desktop · images · contact
2018-03-24
Die Sache mit „wie finde ich meine Dateien“ kam nochmal auf.
Damals dachte ich nur an „normale“ Programme. Kompilierte C-Programme. Das sieht aber alles schon ein bisschen anders aus, wenn man es mit interpretierten Skripten zu tun hat, die über den Shebang-Mechanismus gestartet werden.
Man habe ein einfaches Shellskript:
#!/bin/sh
echo hi
Führt man es aus, …
$ ./my_script foo bar baz
… so wird der Kernel eigentlich die Binary „/bin/sh
“ hinter den
Kulissen ausführen. Das erste Argument wird der Pfad zu unserem Skript
sein, danach kommen die an der Command-Line angegebenen Argumente.
Eigentlich wird also das hier ausgeführt:
$ /bin/sh ./my_script foo bar baz
Und das ist auch schon das ganze Geheimnis. Führt man etwas mithilfe einer Shebang-Zeile aus, dann weiß der Interpreter sehr genau, wie der Pfad zum auszuführenden Skript lautet. Das ist eine Notwendigkeit, weil der Interpreter das Skript die Datei öffnen und lesen muss.
Es ist jetzt also nur noch eine Frage der Weitergabe. Wie kommt diese Information vom Interpreter zum eigentlichen Code?
Die Bash hat eine besondere Variable dafür: „$BASH_SOURCE
“. Angenommen
also, man hat diese Verzeichnisstruktur:
opt/
└── mytool/
├── mytool*
├── some-resource.gif
└── something-else.ogg
Und „/opt/mytool/mytool
“ ist:
#!/bin/bash
my_dir=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
ls -al "$my_dir"
Dann kann man so seine Dateien finden.
Dieser Ansatz ist auch nicht neu und wurde zum Beispiel hier schon skizziert:
Interpreter, die in Shebang-Zeilen erwähnt werden, sind keine besonderen Programmen. Das müssen auch keine tatsächlichen Interpreter sein und man kann da dementsprechend auch leicht etwas eigenes benutzen.
Erstellen wir eine ganz einfache, ausführbare Datei mit nur einer Zeile Inhalt:
#!/opt/mytool/mytool-shebang
Das Ding nennen wir „mytool
“ und platzieren es wieder in
„/opt/mytool
“.
Will man das nun ausführen, so wird das Betriebssystem die Shebang-Zeile
auswerten und dann versuchen, „/opt/mytool/mytool-shebang
“ zu starten.
Erstellen wir diese Datei also. Das kann ein weiteres Skript sein oder
etwas anderes wie zum Beispiel ein C-Programm – nehmen wir mal
letzteres.
#define _XOPEN_SOURCE 500
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
int i;
char *rp;
if (argc < 2)
{
fprintf(stderr, "Need argv[1]\n");
return 1;
}
rp = realpath(argv[1], NULL);
printf("arg 1: '%s', resolved: '%s'\n", argv[1], rp);
free(rp);
for (i = 2; i < argc; i++)
printf("arg %d: '%s'\n", i, argv[i]);
return 0;
}
Übersetzen:
$ cc -std=c99 -Wall -Wextra -o mytool-shebang mytool-shebang.c
Und das war’s im Wesentlichen schon. Man kann jetzt „mytool
“ auf
verschiedene Weisen aufrufen und es wird den richtigen Pfad finden:
$ cd /opt/mytool
$ ./mytool
arg 1: './mytool', resolved: '/opt/mytool/mytool'
$ ./mytool foo bar
arg 1: './mytool', resolved: '/opt/mytool/mytool'
arg 2: 'foo'
arg 3: 'bar'
$ cd /tmp
$ /opt/mytool/mytool
arg 1: '/opt/mytool/mytool', resolved: '/opt/mytool/mytool'
$ cd /tmp
$ PATH=$PATH:/opt/mytool mytool
arg 1: '/opt/mytool/mytool', resolved: '/opt/mytool/mytool'
Dass „realpath()
“ benutzt wird, sorgt dann auch dafür, dass Symlinks
kein Problem sind:
$ cd /tmp
$ ln -s /opt/mytool/mytool
$ ./mytool
arg 1: './mytool', resolved: '/opt/mytool/mytool'
$ cd /tmp
$ rm mytool
$ ln -s /opt/mytool
$ cd mytool
$ ./mytool
arg 1: './mytool', resolved: '/opt/mytool/mytool'
$PATH
“ tun„/opt/mytool/mytool
“ sieht derzeit so aus:
#!/opt/mytool/mytool-shebang
Man kann nun das tun, was viele Python-Skripte tun, und „/usr/bin/env
“
dazu missbrauchen, den Pfad für einen abzuklappern:
#!/usr/bin/env mytool-shebang
Und dann geht das:
$ PATH=$PATH:/opt/mytool mytool
arg 1: '/opt/mytool/mytool', resolved: '/opt/mytool/mytool'
Das alles sollte relativ portabel sein, ausprobiert habe ich es aber nur unter Linux und OpenBSD.
Ich weiß nicht, ob ich einen Hack dieser Art tatsächlich in einem Projekt mal einsetzen würde, weil es sich schon ein bisschen umständlich anfühlt. Untergekommen ist mir das so auch noch nicht. Es ist aber nett, zu wissen, welche Optionen man so hat. :-)