|
KombatElWombat
Anmeldungsdatum: 28. November 2017
Beiträge: Zähle...
|
Ich habe Fragen bezüglich Shell-Variablen.
Die erste ist eher eine Rhetorische, nur um sicher zu gehen das ich es richtig verstanden habe.
Wenn ich mit:
Eine Variable definiere, handelt es sich um eine lokale Variable? Aber wie bekomme ich die Variable wieder gelöscht, ohne das ich einen reboot mache? Der Kontext ist ziemlich simpel (glaub ich), ich soll ein Shell-skript schreiben, das mir folgendes ausgibt: "das aktuelle Verzeichnis enthält 26 Dateien, davon 12 Verzeichnisse"
Meine Idee war nun für die beiden Angaben (Dateien und Verzeichnisse) je eine Variable mit der jeweilig passenden Command-Zeile zu definieren und mir das ganze dann als echo ausgeben zu lassen. Nur hätte ich gerne, nach der Ausführung, diese Variablen wieder gelöscht. Ich hab keine Ahnung ob das Blödsinn ist, aber es kommt mir sauberer vor. PS: wenn möglich bitte nicht Spoilern was das skript angeht, ich mag selber dahinter kommen ☺ Grüße
KombatElWombat
|
|
BillMaier
Supporter
Anmeldungsdatum: 4. Dezember 2008
Beiträge: 6497
|
Dann probier mal selbst aus, ob die Variable nach Aufruf des Skripts definiert ist. 😉 Ob du wirklich richtig liegst, siehst du wenn du hierauf kliegst ▶ Umgebungsvariable
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Generell kannst du mit unset Variablen löschen. Variablen, die in Kind-Prozessen erzeugt wurden, werden nicht an die Eltern weitergegeben - siehe auch http://wiki.bash-hackers.org/scripting/basics Abschnitt "Variable Scope"
|
|
KombatElWombat
(Themenstarter)
Anmeldungsdatum: 28. November 2017
Beiträge: Zähle...
|
BillMaier schrieb: Dann probier mal selbst aus, ob die Variable nach Aufruf des Skripts definiert ist. 😉 Ob du wirklich richtig liegst, siehst du wenn du hierauf kliegst ▶ Umgebungsvariable
Scheiße, kapiert, danke für den Hinweis ☺
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
|
KombatElWombat schrieb: Ich habe Fragen bezüglich Shell-Variablen.
Immer her damit ! Eine Variable definiere, handelt es sich um eine lokale Variable?
Ähm - ja. "lokal" ist sie in der Hinsicht, dass sie nur für die Shell gilt, in der sie definiert wurde. (sie wird niemals an Eltern- oder Geschwisterprozesse vererbt !) "global" ist sie in der Hinsicht, dass sie sehr wohl an alle Tochterprozesse weitervererbt wird. (wenn Du es ganz genau wissen musst, hilft nur der Blick ins Bash-Manual !) Aber wie bekomme ich die Variable wieder gelöscht, ohne das ich einen reboot mache?
Mit unset ? Der Kontext ist ziemlich simpel (glaub ich), ich soll ein Shell-skript schreiben, ...
Dann lohnt sich ganz sicher ein Blick auf die Shell-Befehlsliste, und wahrscheinlich auch in unser Wiki ... 😉 LG, track
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4735
|
@track: Was verwendest Du denn für eine Shell die lokale Variablen einfach so an Kindprozesse vererbt?
$ hallo='test'
$ bash -c 'echo "$hallo"'
$
In den Shells die ich verwende muss ich Variablen explizit exportieren, damit sie auch in Kindprozessen gesehen werden.
$ export hallo
$ bash -c 'echo "$hallo"'
test
$ unset hallo
$ bash -c 'echo "$hallo"'
$
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
|
Marc_BlackJack_Rintsch schrieb: @track: Was verwendest Du denn für eine Shell die lokale Variablen einfach so an Kindprozesse vererbt?
Nein, meine Shell braucht natürlich auch ein export , wenn sie Variablen an die Kinder vererben soll. Nur an Eltern und Geschwister vererben, das geht niemals. Nur - wo BillMaier mit seinem Link auf das Wiki gerade vorher darauf hingewiesen hatte, fand ich es überflüssig, auch noch einmal darauf herum zu reiten. Es muss ja nicht alles von jedem gesagt werden ... Es war als Ergänzung gemeint, nicht als Korrektur. LG, track
|
|
KombatElWombat
(Themenstarter)
Anmeldungsdatum: 28. November 2017
Beiträge: Zähle...
|
Mir schwimmt etwas der Kopf von dem ganzen Prozess-Inzest ... aber zumindest halbwegs habe ich es kapiert.
Das Skript
| #!/bin/bash
echo "Das aktuelle Verzeichnis enthält" ; ls -l | wc -l ; echo "Dateien, davon" ; ls -l | grep '^d' | wc -l ; echo "Verzeichnisse."
|
Gibt mir das aus:
| $ ./anzDat
Das aktuelle Verzeichnis enthält
58
Dateien, davon
18
Verzeichnisse.
|
Wie bekomme ich es in eine Zeile? Ich verstehe zwar (fast) was ihr mir alle sagt, ich finde das sogar teilweise in dem Buch wieder nach dem ich gerade lerne, aber erst viel später. Die Aufgabe sollte tatsächlich eine Einführung sein, mir kommt sie aber langsam wie ein Einlauf vor 😕
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4735
|
@KombatElWombat: Was ist aus dem Plan geworden die Ergebnisse zu ermitteln und an Variablennamen zu binden? Dann wäre die Ausgabe ohne Zeilenumbrüche nicht nur einfacher sondern der Code auch lesbarer. Ansonsten schau mal in der man-Page von echo nach wie man die Ausgabe des Zeilenumbruchs verhindern kann.
Edit: grep kann übrigens selbst schon zählen, da kann man sich das wc sparen. Und das alles funktioniert nicht mehr wenn Datei- oder Verzeichnisnamen Zeilenumbrüche enthalten. Robust könnte das beispielsweise so aussehen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | #!/bin/bash
Main() {
local totalCount=0
local directoryCount=0
while IFS='' read -r -d '' filename; do
(( totalCount++ ))
if [[ $(stat -c '%F' "$filename") = 'directory' ]]; then
(( directoryCount++ ))
fi
done < <(find . -maxdepth 1 -print0)
echo "Das aktuelle Verzeichnis enthält $totalCount Einträge," \
"davon $directoryCount Verzeichnisse."
}
[[ ${BASH_SOURCE[0]} = "$0" ]] && Main "$@"
|
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
|
Die Ausgabe von ls zu parsen ist heikel. → Du solltest bei ls wenigstens die Option -b hinzufügen ! Solche Sachen werden grundsätzlich recht umständlich mit der Shell (die ist nicht wirklich dafür gemacht !), deshalb würde ich die Einträge von ls -lb anders zählen. Mein Kandidat wäre awk: track@track:~$ ls -lb ./-test | awk '/^-/ {dat++} /^d/ {verz++}
END {print "Das Verzeichnis enthält",verz,"Verzeichnisse und",dat,"Dateien."}'
Das Verzeichnis enthält 5 Verzeichnisse und 23 Dateien. Solcher Code ist sehr viel übersichtlicher: man sieht sofort das Hochzählen und die formatierte Ausgabe. (mehr ist es ja nicht !) - ach ja: noch die Zeilenauswahl mit den Regulären Ausdrücken ... p.s.: willst Du womöglich auch die versteckten .Dateien mitzählen ? → dann musst Du das ausdrücklich sagen, z.B. mit der Option ls -a .
LG, track
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4735
|
Wenn man -a noch dazu packt, darf man aber nicht vergessen . und .. nicht mit zu zählen.
|
|
KombatElWombat
(Themenstarter)
Anmeldungsdatum: 28. November 2017
Beiträge: 11
|
Marc_BlackJack_Rintsch schrieb: @KombatElWombat: Was ist aus dem Plan geworden die Ergebnisse zu ermitteln und an Variablennamen zu binden? Dann wäre die Ausgabe ohne Zeilenumbrüche nicht nur einfacher sondern der Code auch lesbarer. Ansonsten schau mal in der man-Page von echo nach wie man die Ausgabe des Zeilenumbruchs verhindern kann.
Danke, pedagogisch wertvoll! ☺ Mein Skript ist nicht so schick wie eure aber es funktioniert. | #!/bin/bash
anzdat1=$(ls -l | wc -l)
anzdat2=$(ls -l | grep -c '^d')
echo Das Verzeichnis enthält $anzdat1 Dateien, davon sind $anzdat2 Verzeichnisse\.
|
Ich war total auf dem Holzweg und hab mir mehr Probleme gemacht, als ich eigentlich hatte 😕
Achso, falls jemand noch was sieht, an meiner Lösung, das evtl Probleme machen könnte, bitte nur zu!
|
|
Marc_BlackJack_Rintsch
Ehemalige
Anmeldungsdatum: 16. Juni 2006
Beiträge: 4735
|
@KombatElWombat: Mir ist nicht so ganz klar warum Du den Punkt escapest, aber ich würde da ja eher echo nur ein Argument übergeben und nicht neun. Die Formulierung ist auch etwas komisch. x Dateien von denen y Verzeichnisse sind? Das würde ja bedeuten das es Dateien gibt die gar keine Dateien sind, nur sind das dann ja keine Dateien, also sollten die auch nicht als solche gezählt werden. Das Verzeichnisse tatsächlich mal Dateien waren, ist schon etwas länger her, und ich glaube bei Linux war das nie der Fall. Ich würde statt Dateien Einträge oder Verzeichniseinträge sagen. Edit: Ach ja, und Deins funktioniert nicht so 100%ig, denn es verzählt sich bei Einträgen mit Zeilenumbrüchen im Namen.
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11278
|
Marc_BlackJack_Rintsch schrieb: Edit: Ach ja, und Deins funktioniert nicht so 100%ig, denn es verzählt sich bei Einträgen mit Zeilenumbrüchen im Namen.
Außerdem findet es keine versteckten Dateien und Verzeichnisse (zumindest solange dotglob nicht als Shell-Option gesetzt ist). Man könnte auch find bemühen, das kann Dateien und Verzeichnisse auseinander halten:
| echo "Das Verzeichnis enthält $(find -maxdepth 1 -type f -printf "\n" | wc -l) Dateien und $(($(find -maxdepth 1 -type d -printf "\n" | wc -l) -1)) Verzeichnisse."
|
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
|
KombatElWombat schrieb: ... Achso, falls jemand noch was sieht, an meiner Lösung, das evtl Probleme machen könnte, bitte nur zu!
Ähm .... die Kommentare zum Parsen von ls -l die ich oben gab, gelten ausdrücklich auch für Deine Variante ! (→ die Sache mit den Optionen ls -a und ls -b !) Ansonsten könntest Du alternativ die ganze Arbeit auch allein von der Shell erledigen lassen, mit Filename Expansion in 2 Arrays, und dann die Argumente zählen: track@track:~$ f=( ./-test/* )
track@track:~$ v=( ./-test/*/ )
track@track:~$ echo "Das Verzeichnis enthält ${#f[*]} Einträge, davon ${#v[*]} Verzeichnisse."
Das Verzeichnis enthält 28 Einträge, davon 5 Verzeichnisse. Funktioniert perfekt ... - direkt intuitiv verständlich ist das eher nicht, wohl aber ganz puristisch "die reine Shell". 😀 LG, track
|