staging.inyokaproject.org

Lokale Shell Variablen löschen

Status: Gelöst | Ubuntu-Version: Ubuntu 16.04 (Xenial Xerus)
Antworten |

KombatElWombat

Anmeldungsdatum:
28. November 2017

Beiträge: 11

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:

1
hallo='ls -l'

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 Team-Icon

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: 11

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

Avatar von 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 Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

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

Avatar von 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: 11

Mir schwimmt etwas der Kopf von dem ganzen Prozess-Inzest ... aber zumindest halbwegs habe ich es kapiert. Das Skript

1
2
#!/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:

1
2
3
4
5
6
$ ./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 Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

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

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

  1. Die Ausgabe von ls zu parsen ist heikel. → Du solltest bei ls wenigstens die Option -b hinzufügen !

  2. 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 ...

  3. 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 Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

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.

1
2
3
4
#!/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 Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

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:

1
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

Avatar von 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

Antworten |