Wie kann ich pro Aufruf die jeweils nächste Zeile einer Datei auslesen?

F

franzjosef

Grünschnabel
Hallo zusammen,

ich hoffe ihr könnt mir helfen. Und zwar bin ich auf der Suche nach einer Möglichkeit, wie man Zeilenweise aus einer Datei lesen kann.

Am besten ich erkäre es an einem Beispiel.
Ich möchte mit wget über mehrere Proxies Dateien herunterladen. Das DL-Skript habe ich schon, allerdings ohne Proxy-Funktion.

Deshalb muss in dieses Bash-Skript eine Funktion, die mir aus einer Liste Proxy.txt den jeweils nächsten Aufruft. Sprich wenn der DL- fertig ist, dann wird danach die neue Funktion aufgerufen.

Ich habe mir die neue Funktion wie folgt vorgestellt:
Die Funktion liest aus der Proxy.txt die erste Zeile und schreibt mit
Code:
export http_proxy=111.111.111.111:8080
den Proxy für wget. Damit die Funktion weiß, welche Zeile die Funktion beim letzten Aufruf genommen hat, merkt sich die Funktion die Zeile 1 in einer Variablen/Datei.
Beim nächsten Aufruf schaut die Funktion, welche Zeile das letzte Mal benutzt wurde und benutzt die jeweils nächste.

Sprich die Funktion liest die zweite Zeile und schreibt ein
Code:
export http_proxy=222.222.222.222:8080

Wie könnte ich mein Vorhaben am besten bewerkstelligen?

Viele Grüße
Martin
 
Meinst du sowas:
Code:
while read i; do echo "WAS SOLL ICH MIT ${i} machen?"; done < proxyliste.txt

Oder habe ich das doch falsch verstanden?
 
also wenn ich deine zeile richtig interpretiere, dann wird ja für jede zeile was gemacht.

die funktion, die ich bräuchte, soll nur bei bedarf ausgeführt werden.
also nicht pro zeile in proxliste.txt soll was ausgeführt werden.

Zur Erläuterung:
wget kann ja nur mit einem Proxy umgehen. die Proxy-Adresse nimmt wget aus der variablen http_proxy

Du kannst Dir vorstellen, dass wenn das skipt gestartet wird, dann wird die neue Funktion das erste mal aufgerufen. Bei diesem ersten Aufruf liest es die erste Proxy-Adresse und schreibt diese in die variable http_proxy.

Dann kommen wir in eine Schleife.
In der Schleife werden mit Wget nun mehrere Dateien heruntergeladen. Neben dem DL mit wget finden auch noch andere Prüfungen statt. Wenn nun diese Prüfung ergibt, dass der Proxy gewechselt werden muss, dann soll die neue Funktion das zweite Mal aufgerufen werden.

Die Funktion soll nun die Variable http_proxy mit der zweiten Zeile aus der ProxyListe.txt belegen. Dazu muss die Funktion ja aber wissen, dass bei ihrem vorherigen Aufruf die Zeile 1 bereits benutzt wurde und nun eben die zweite Zeile dran ist.

Danach macht die DL-Schleife weiter, bis wieder eine Bedingung eintritt, dann wird die Funktion erneut aufgerufen, die dann eben die dritte Zeile finden soll.

usw.

Viele Grüße
Martin
 
Zuletzt bearbeitet:
Code:
grep -A1 http_proxy=111.111.111.111:8080
 
Du kennst sed?
Dort läßt sich direkt die Zeile, die ausgegeben werden soll, adressieren.
Du musst also nur eine Variable hochzählen und diese dann sed als Adresse übergeben.


BTW sieht dein Vorhaben irgendwie nach Verwendung von anonymen Proxys aus, um illegale Aktionen zu vertuschen. Ich las so eine ähnliche Anfrage schon einmal in einem Forum. Wenn das der Fall ist, rate ich dir, dich nicht in falscher Sicherheit zu wiegen.
Das sind zu 99% nur falsch konfigurierte Proxys, die von jetzt auf gleich transparent werden. ;)
Wenn nicht, setze meinen Gedanken einfach um.

Wolfgang
 
Du kennst sed?
Dort läßt sich direkt die Zeile, die ausgegeben werden soll, adressieren.
Du musst also nur eine Variable hochzählen und diese dann sed als Adresse übergeben.

nein, leider nicht.
wenn ich bisher was geskriptet habe, dann war das batch unter dos. da ich nun immer mehr linux-geräte im haus habe, muss ich mich da jetzt auch mal mit beschäftigen.
in batch gibt es leider keinen befehl, der mir genau eine bestimmte zeile aus einer datei ausliest. das musste ich mir ganz umständlich selber bauen.

wie würde denn mit sed die skript-zeile aussehen, um z.B. die 3. Zeile einer datei liste.txt in eine variable http_proxy zu schreiben?

Viele Grüße
Martin
 
Der Stream-Editor Sed ist aus der shell-Programmierung und wird folgendermassen beschrieben:
sed ist ein relativ altes Tool.wird aber immer noch eingesetzt, etwa um Zeilen einer Datei oder eines Datenstromes zu manipulieren. Besonders haeufig und gern wird sed zum Suchen und Ersetzen von Zeichenfolgen verwendet.
Na, das ist doch was.
sed (adresse1),(adresse2) 'komanndo' (datei(en)::: Komando muss alles andere kann
adrese1 = start/ adresse2 = Ende
Ein ! hinter adresse1 und 2 bedeutet, das der Bereich von 1 bis 2 ausgeschlossen ist. Komanndo gilt also nur fuer den Rest. Mit -n wird der Default Output von sed abgeschaltet

sed -n '4p' file.dat liest nur die 4. Zeile aus file.dat
sed -n '4,7p' file dat liest Zeile 4 bis 7 . sed -n '7,4p' file dat wird ignoriert (Bis groesser als von = ruechwaerts lesen)

Komanndo Bedeutung
a (append) fuegt eine oder mehrere Zeilen an
c (change) ersetzt die Zeile durche eine oder mehrere andere
d (delete) loescht die Zeile
g (get buffer) kopiert in temporeren Puffer
G (getNewline) fuegt g an den Arbeitspuffer
h (hold) Gegenteil von g -Kopiert G zu g
H (HoldNewline) Gegenstueck zu G
i (insert) fuegt zeile vor der selektierten zeile
l (listing) zeigt nicht druckbare Zeichen an
n (next) Verwendet das naechste Komanndo auf die
naechste Zeile
p (print) Gibt die Zeile aus
q (quit) beendet sed
r (read) Datei integrieren.liest Zeilen aus eine Datei ein
s (substitut) Ersetzt eine Zeichenfoge
x (XChange) vertauscht temporeren Puffer mit Arbeitspuffer
y (yank) ersetzt ein Zeichen
w (write) schreibt zeilen in einer Datei
! Negation: verwendet Komanndo auf andere Zeilen

Sehen wir mal, ob Du damit etwas anfangen kannst
Gruss
Yomm
 
Wie kann ich pro Aufruf die jeweils nächste Zeile einer Datei auslesen?
da du keine bestimmte, sondern jeweils die nächste zeile lesen willst, nochmal mein vorschlag mit grep.
Code:
newproxy=$(grep -A1 $oldproxy proxylist)
echo $newproxy
damit erhälst du immer den jeweils nächsten nach dem zuletztgenutzen proxy.

erscheint mir am einfachsten
 
Na, das ist doch was.
sed -n '4p' file.dat liest nur die 4. Zeile aus file.dat

stimmt mit
Code:
newproxy=$(sed -n '4p' list.txt)
echo $newproxy

list er mir die 4. Zeile aus der Datei list.txt, schreibt diese in die Variable newproxy und mit echo schreibt er mir auch das richtig auf den bildschirm.

aber wenn ich hier schaue, müsste es dann nicht
Code:
newproxy=$(sed -n '/4/p' list.txt)
heißen. Wie ist denn das mit den '/'-Zeichen?







Code:
newproxy=$(grep -A1 $oldproxy proxylist)
echo $newproxy

Hört sich für mich schon recht schön an, aber irgendwie mache ich doch wohl was falsch.

Inhalt der Datei list.txt:
Code:
111
222
333
444
555

dass ich die Variable oldproxy mit
Code:
oldproxy=111
erst deklarieren muss, das habe ich nun herausgefunden. sonst läuft das ganze nicht. ich muss der Variablen auch den ersten Wert mitgeben, sonst läuft es auch nicht. Sprich ein
Code:
oldproxy=
reicht leider nicht.

Wenn ich nun ein
Code:
newproxy=$(grep -A1 $oldproxy list.txt)
dann wird daraus aber immer nur ein
Code:
111 222

Was soll denn dieses -A1 genau machen?


Belege ich
oldproxy=222
dann hängt der Aufruf von
Code:
newproxy=$(grep -A1 $oldproxy list.txt)
wieder

Was mache ich falsch?

Viele Grüße
Martin
 
Hallo
Also nochmal langsam:


Wenn ich nun ein
Code:
newproxy=$(grep -A1 $oldproxy list.txt)
dann wird daraus aber immer nur ein
Code:
111 222

Was soll denn dieses -A1 genau machen?

Das -A bei grep gibt dir den Treffer + die darauf folgende Zeile an.
Insofern nicht das was du willst.
Es sei denn, du verwendest es in einem Array und greifst auf den Index 1 zu. (was in der Bash geht)

Dein Vorhaben ist mir ehrlich gesagt etwas suspect.
Warum postest du nicht den Beispielcode, dann lässt sich das Ganze weniger abstrakt erklären.

Code:
sed -n '/4/p' -> matcht auf 4 und printet wenn gefunden
sed -n '4p'  -> Gibt nur die Zeile 4 aus
Siehst du den Unterschied?
Zu deinem Problem:
Du zählst einfach die Zeilen in einer Variablen, und wenn du die nächste Zeile brauchst, übergibst du sie sed als Adresse.
Wobei du dann doppelte Hochkomma verwenden musst, damit diese von der Shell aufgelöst werden kann.

Aber wie gesagt: Poste deinen Code hier und deine Fragen werden konkreter.

Wolfgang
 
Warum postest du nicht den Beispielcode, dann lässt sich das Ganze weniger abstrakt erklären.

Code:
sed -n '/4/p' -> matcht auf 4 und printet wenn gefunden
sed -n '4p'  -> Gibt nur die Zeile 4 aus
Siehst du den Unterschied?

Hallo,

ehrlich gesagt versteh ich den Unterschied nicht. was meinst Du mit matcht?

Hier mal mein Pseudo-Code von dem, was ich brauche:

Code:
proxyline=1

do
export http_proxy=$(http://[sed -n ($proxyline)p proxylist.txt])
echo $http_proxy
echo $proxyline
proxyline=$(($proxyline+1))

if proxylist.txt is eof then proxyline=1

while [proxylist.txt is not eof]

done

Sprich am Anfang wird ein Zähler proxyline erst mal auf 1 initialisiert.
Dann läuft das ganze in eine Schleife. Solange nicht das Ende der Datei proxylist.txt erreicht ist, soll folgendes passieren.

In der Datei proxylist.txt stehen ip-Adressen in der Form
111.111.111.111:9999
222.222.222.222:8888

Nun soll pro Schleifendurchlauf die Variable http_proxy belegt werden.
Die Variable muss dabei im erstn Schleifendurchlauf wie folgt belegt werden:

http://111.111.111.111:9999

Im zweiten Durchlauf soll folgender Wert angenommen werden:

http://222.222.222.222:8888

usw.

Nachdem die Variable belegt ist, wird für mich erst einmal zur Kontrolle die Werte für die beiden Variablen ausgegeben.
Dann wird der Zähler proxyline eins hochgesetzt.

Ich dachte mir nun, dass ich die Variable wie folgt zusammenbaue.
1. zuerst das fixe http://
2. mit sed -n '@p' proxylist.txt die ip-Adresse + den Port auslese, wobei @ für den Zähler, also die Variable proxylist steht
3. das ganze dann komplett in die Variable http_proxy schreibe

Wenn eof erreicht wird, soll der Zähler wieder auf 1 gesetzt werden, damit es wieder von vorne losgeht, sprich die while-Schleife soll eine Endlosschleife sein.

Was muss ich da tun, damit das funktioniert?

Viele Grüße
Martin
 
Zuletzt bearbeitet:
Hallo,

ehrlich gesagt versteh ich den Unterschied nicht. was meinst Du mit matcht?

Hier mal mein Pseudo-Code von dem, was ich brauche:
Das bedeutet, wenn in der Zeile eine 4 vorkommt, dann wird diese ausgegeben. Dass das nicht zwangsläufig die vierte Zeile ist, ist wohl klar.



Code:
proxyline=1

while [proxylist.txt is not eof]; do
export http_proxy=$(http://[sed -n ($proxyline)p proxylist.txt])
echo $http_proxy
echo $proxyline
proxyline=$(($proxyline+1))

done

Grausam ...
Was muss ich da tun, damit das funktioniert?

Dich erstmal mit den Grundlagen der Bash oder was immer du verwendest auseinandersetzen.

Hier ein Link Klick und lesen

Besonders für dich interessant:
Schleifen, Redirect, Incrementierung von Variablen, sed

Wir sind hier keine Scriptfabrik, deshalb nur die nötigen Werkzeuge:

Code:
i=1;
for URLS in $(cat deine_urlliste);
do

((i++)); # i hochzählen
MY_PROXY='http://'$(sed -ne "${i}p" LISTE_DER_PROXYS);
...
echo "$MY_PROXY"
...
wget ....
done
Ob du den Proxy wechseln willst oder nicht, kannst du vor dem Increment von i ja testen.
 

Gar nicht schlecht die Übersicht. Ein gutes Nachschlagewerk.

Ich komme aus der DOS-Welt, da ist doch einiges anders. Aber da bei mir immer mehr Geräte mit Linux als Betriebssytem Einzug halten, muss ich mich auch mal mit bash beschäftigen.

Wie nennt man sowas hier:

Code:
test() {
echo test
}

Ist das eine Funktion oder heißt das Sprungmarke wie in einer Batch?

Wenn ich nun aus so einer Funktion/Sprungmarke eine andere Aufrufe und diese zweite ist beendet, springt er dann wieder an den Aufruf in der ersten funktion zurück?

Bsp.:

Code:
Funktion1(){
echo "Ich bin da"
Funktion2
echo "bin wieder zurueck in Funktion1"
}

Funktion3(){
echo "bin jetzt in f3"
}

Funktion2(){
echo "wie gehts dir"
}

Funktion4(){
echo "Funkion4"
}

Werden solche Funktionen eigentlich im Ablauf des skripts eigentlich ausgeführt oder nur dann, wenn sie explizit aufgerufen werden?

Ich weiß, viele Anfängerfragen. Aber jeder hat mal angefangen.

Viele Grüße
Martin
 
Hallo

Bevor ich jetzt Dinge wiederhole, die schon von Anderen aufgeschrieben wurden:
Lies Kapitel 8

Funktionen werden vorher definiert, oder per source aus anderen Initscripten in den Namensraum geholt.
DOS Batch ist im Gegensatz zur Shell unter *nix eine Hilfskrücke.
Wenngleich es auch da wenige Akrobaten unter Programmierern gab/gibt.

Gruß Wolfgang
 
Hallo Wolfgang,

so bin wieder ein Stückchen weiter. Das mit dem sed ist aber kompliziert.
Irgendwie ist mir da nicht so einfach, wann welche Klammer gesetzt werden muss und wann das Hochkomma und wann die Gänsefüßchen hermüssen.

An Klammern gibt es ja drei Stück
()
[]
{}
Das mit den geschweiften Klammern, das taucht hier gar nicht erst auf.

In der Doku steht auch, dass es keinen Unterschied macht, ob man das Hochkomma oder die Gänsefüßchen verwendet. Aber irgendwie glaube ich, dass dem nicht so ist.
Kann es sein, dass wenn sich in dem Bereich etwas variables befindet (also entweder eine Variable (wie oben z.B. das i), dass man dann besser die Gänsefüßchen nimmt?

Hier mal mein Zwischenergebnis:
Code:
Durchlauf=1
proxyline=1

# Schleife bricht ab, wenn Durchlauf <= 20
while [ $Durchlauf -le 20 ] ; do
        echo "Durchlauf hat jetzt den Wert $Durchlauf"
		
lastProxy=$(wc -l <proxylist.txt)

if ["$proxyline" -lt "$lastProxy"]; then
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=$(($proxyline+1));
	echo "$http_proxy"
else
	proxyline=1
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=$(($proxyline+1));
	echo "$http_proxy"
fi
		
        Durchlauf=$(($Durchlauf+1))
done

Irgenwo klemmt's noch. Kannst Du bitte mal schauen, wo ich den Fehler gemacht habe? Ist bestimmt nur wieder eine klammer oder so

Viele Grüße
Martin
 
Zuletzt bearbeitet:
Hallo
Die "" oder ' ' sind auch keine Option von sed, sondern verhindern oder erlauben die Variablenexpansion der Shell.
Wenn du also eine Variable by Value aus der Shell an sed übergeben willst, verwende " " .
Sonst ' ' .

Zu der ${VARNAME}
${parameter}
The value of parameter is substituted. The braces are required when parameter is a positional parameter
with more than one digit, or when parameter is followed by a character which is not to be interpreted as
part of its name.

Wird da oben nur verwendet, um den sed-Befehl p (print) vom Variablennamen abzutrennen.

Zu deinem Skript:
Was geht denn nicht?

Wolfgang
 
Zu deinem Skript:
Was geht denn nicht?

Hallo Wolfgang,

ich habe jetzt noch ein paar echos rein, damit man genau sieht, wo er gerade ist:
Code:
#!/bin/ash

Durchlauf=1
proxyline=1

# Schleife bricht ab, wenn Durchlauf <= 20
while [ $Durchlauf -le 20 ] ; do
        echo "Durchlauf hat jetzt den Wert $Durchlauf"
		
lastProxy=$(wc -l <proxylist.txt)

if ["$proxyline" -lt "$lastProxy"]; then
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=$(($proxyline+1));
	echo "$http_proxy"
	echo $proxyline
	echo $lastProxy
	echo 111
else
	proxyline=1
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=$(($proxyline+1));
	echo "$http_proxy"
	echo $proxyline
	echo $lastProxy
	echo 222
fi
		
        Durchlauf=$(($Durchlauf+1))
done

Wenn ich das ganze ausführe, dann meldet er immer
Code:
/var/tmp #
/var/tmp # ./test.sh
Durchlauf hat jetzt den Wert 1
./test.sh: line 30: [1: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 2
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 3
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 4
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 5
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 6
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 7
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 8
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 9
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2
4
222
Durchlauf hat jetzt den Wert 10
./test.sh: line 30: [2: not found
http://116.7.254.148:8080
2

Es sind mehrere Dinge, die nicht passen.

1. Die Variable http_proxy nimmt immer nur den Wert aus dem Wert aus Zeile eins der Datei proxylist.txt an, eben die http://116.7.254.148:8080
2. es erscheint der Fehler line 30: [2: not found
3. die Variable proxyline bleibt auf zwei und wird nicht zurückgesetzt auf 1, wenn die letzte Zeile erreicht wurde.
 
Zuletzt bearbeitet:
Schau dir nochmal die Syntax von if [ an.
[ ist in der bash üblicherweise ein Alias auf test.
Dieser muss immer von Leerzeichen umgeben sein.
Code:
if [<LEERZEICHEN>"$VAR"  ...<LEERZEICHEN>];...

Weiterhin genügt es zum Incrementieren :
Code:
proxyline=$(($proxyline+1));
# Kurzform
((proxyline++));

Wolfgang
 
Schau dir nochmal die Syntax von if [ an.
[ ist in der bash üblicherweise ein Alias auf test.
Dieser muss immer von Leerzeichen umgeben sein.
Code:
if [<LEERZEICHEN>"$VAR"  ...<LEERZEICHEN>];...

stimmt, Du hast recht. fügt man Leerzeichen ein, dann funktioniert. Ich wusste doch, wieder die Klammern. ;-)

Weiterhin genügt es zum Incrementieren :
Code:
proxyline=$(($proxyline+1));
# Kurzform
((proxyline++));

Ich weiß, das geht so in fast jeder Sprache, aber irgendwie wird die Kurzform nicht ausgeführt. Keine Ahnung warum.
Kann es daran liegen, dass das Skript auf einem Linux-embedded-Gerät ausgeführt wird?

Auf jeden Fall, jetzt funktionierts.



Jetzt wäre ich beim nächsten Kapitel, Funktionen, weil das ganze ja später mal in einem anderen Skript zum Einsatz kommen soll, das aus lauter Funktionen besteht.
Dazu packe ich den "Proxy-Sucher-Teil" in eine While-Schleife. Die While-Schleife simuliert das abarbeiten des bereits vorhandenen Skriptes. Sprich der "Proxy-Sucher-Teil" kommt als Funktion in das bereits vorhandene Skript und wird dort an verschiedenen Stellen aufgerufen.
Mit den echo-Ausgaben will ich sehen, an welcher Stelle die Variable existiert bzw. mit welchem Wert diese belegt ist.

Mein Testskript sieht jetzt so aus:
Code:
#!/bin/ash

# Globale Variable
Durchlauf=1
proxyline=1
http_proxy=start

# Schleife bricht ab, wenn Durchlauf <= 1
while [ $Durchlauf -le 1 ] ; do
        echo "Durchlauf hat jetzt den Wert $Durchlauf"
# Variable http_proxy?
echo $http_proxy >> vor.txt

# Funktion ausführen
testi
echo $http_proxy >> nach.txt

Durchlauf=$(($Durchlauf+1))

# Funktion
testi () {
    lastProxy=$(wc -l <proxylist.txt)

if [ "$proxyline" -lt "$lastProxy" ]; then
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=$(($proxyline+1));
	echo $http_proxy >> mitte.txt
else
	http_proxy='http://'$(sed -ne "${proxyline}p" proxylist.txt);
	proxyline=1
	proxyline=$(($proxyline+1));
	echo $http_proxy >> mitte.txt
fi
}
    
done

Jetzt müsste die Schleife genau ein mal durchlaufen.
Ich lasse mir den Wert der Variablen http_proxy einmal vor der Ausführung der Funktion (vor.txt), dann in der Funktion selbst (Mitte.txt) und nach der Ausführung der Funktion (nach.txt) schreiben.

Wenn ich die Schleife auf einmal durchlaufen begrenze, dann wird nur die vor.txt und die nach.txt geschrieben.
Die mitte.txt aber nicht.
Es kommt auch die Meldung
line 36: testi: not found

Kannst Du mir sagen, was da schiefläuft?
Es steht doch alles in dem Schleifen-Körper drinn.



Viele Grüße
Martin
 
Zuletzt bearbeitet:

Ähnliche Themen

Samba als PDC [Short-HOWTO]

Zurück
Oben