• Diese Seite verwendet Cookies. Indem du diese Website weiterhin nutzt, erklärst du dich mit der Verwendung von Cookies einverstanden. Erfahre mehr

Shellvariable nicht global genug

Krull

Well-Known Member
Themenstarter #1
Hallo,
ich möchte mehrere Internetseiten gleichzeitig abfragen und deren Inhalt in einer Variable ablegen.

Dazu mache ich erstmal das:
Bash:
   i=1
   while (( i <= $#URL )); do
        fetch $i "${URL[$i]}" &
        (( i++ ))
    done
fetch sieht so aus:
Bash:
fetch()  {
    tmp[$1]="$(curl "$2")"
    #echo "${tmp[$1]}"
    return 0
}
Wenn ich $tmp in der fetch-Funktion ausgebe, bekomme ich auch den Inhalt zu sehen. ABER, wenn ich später im Hauptprogramm die Variable auswerten möchte, ist sie leer. Ich weiß nicht, wie ich das Ding global bekomme. 'declare -ag' habe ich schon versucht. Kann das daran liegen, dass fetch in den Hintergrund geschickt wird und die Variable dann auch nicht mehr zurück darf? Was kann man da tun? Die Setenabrufe sollen auf jeden Fall parallel laufen. Sequentiell ist das viel zu lahm...
 
#2
Durch das & wird ein neuer Shell-Prozess gestartet, und die Parent-Shell bekommt nix mit von den Änderungen von Variablen der Child-Shell. Aber auch sonst geht dein Ansatz für mich noch nicht so Recht auf, denn woher willst du wissen, wann alle URLs abgerufen wurden?

Wenn es für dich okay ist, die runtergeladenen Daten vorübergehend in temporären Dateien zu speichern, würde das funktionieren:

Bash:
nl >urls.txt <<EOF
https://www.duckduckgo.com/
https://www.ecosia.com/
https://www.openbsd.org/
https://www.netbsd.org/
https://www.freebsd.org/
EOF
xargs <urls.txt -P 3 -n 2 curl -o
Mit nl und dem Here-Dokument schreibe ich die Adressen und zugehörige Nummern (1-5) in die urls.txt. Diese dient als Eingabe für xargs. Den Teil kannst du natürlich auch anders gestalten, je nachdem wie deine Adressen vorliegen. Die Option -n 2 sorgt dafür, dass jedem curl-Aufruf 2 Parameter übergeben werden (die Nummer und die URL). Mit -P 3 werden 3 Prozesse parallel ausgeführt, das kannst du beliebig erhöhen. Danach sind die Daten in den Dateien mit den Namen 1, 2, 3 usw. Die müsstest du dann eben zur weiteren Verarbeitung auslesen. Wenn du noch Fragen hast, melde dich bitte nochmal. :)
 

Krull

Well-Known Member
Themenstarter #3
Wann eine Seite abgerufen wurde, weiß ich wenn die Variable nicht mehr leer ist. Also ungefähr:
Bash:
while : ; do
    if [[ -n ${tmp[$i]} ]]; then
        ...
    fi
    ...
done
Temporäre Dateien habe ich bis dato benutzt. Aber davon wollte ich eigentlich weg. Aktuell benutze ich named pipes, um den Seiteninhalt aus den Unterprozessen wieder ins Hauptprogramm zu bekommen. Das funktioniert, nur muss ich dazu trotzdem wieder Dateien auf der Platte anlegen (wenn auch sehr kleine).
Eigentlich wäre 'coproc' genau das, was ich brauche. Nur leider kann man anscheinend nicht mehr als einen Co-Prozess pro Shell haben, was den Nutzen davon wieder erheblich reduziert.
 
#4
Vorsicht, die Variable wird für immer leer bleiben, wenn der Seitenabruf fehlschlägt. Falls das für deinen Anwendungsfall kein Problem ist, um so besser.
 

Krull

Well-Known Member
Themenstarter #5
Ja, hinter ... versteckt sich u. a. noch die Prüfung auf einen Timeout, die das Programm ggf. beendet.
 

Andy_m4

Well-Known Member
#6
Solche Sachen zeigen mir immer wieder überdeutlich, warum ich Shellprogrammierung nicht mag und lieber (so es geht) auf eine "richtige" Programmiersprache zurückgreife. Da wäre das alles kein Thema.
 
#7
Ich denke, Krull geht es hier eher darum, auszutüfteln wie man das Problem auf eine spezifische Weise lösen kann, nicht wie man es am einfachsten löst - Was ja völlig legitim ist, da man ne Menge dabei lernt.

Ein Timeout ist da halt auch nur ein Hack, weil er nicht tatsächlich auf Erfolg oder Fehler überprüft. Würde ich persönlich so nicht machen.