ubuntuusers.de

bash scripting mit dictonary oder ähnlichen

Status: Gelöst | Ubuntu-Version: Xubuntu 24.04 (Noble Numbat)
Antworten |

matze31

Anmeldungsdatum:
25. Oktober 2015

Beiträge: 836

Hallo, gibt es eine Möglichkeit, in bash so eine Art dictonary zu deklarieren und diese dann in eine Datei zu schreiben. Um später dann wieder drauf zugreifen zu können und den x-ten Wert zu ändern?

Beispiel:

Ich habe name1=aaa, name2=bbb, name3=ccc u.s.w. Jetzt möchte ich aber den Wert name2 ändern von bbb auf ddd.

Die Datei muss auch für anderen Scripte erreichbar sein.

Wie kann man so etwas realisieren?

verdooft

Anmeldungsdatum:
15. September 2012

Beiträge: 4578

Wenn was externes klar geht, ist in den Quellen:

crudini - edit ini files from the command line shell

http://www.pixelbeat.org/programs/crudini/

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17672

Wohnort: Berlin

Ja, und ja.

a) Wenn Du selbst Buch führst, kannst Du schreiben:

(Dictionary als File anlegen)

1
2
3
echo "a=foo
b=7
c=/dev/cdrom" > conf.sh

Die Werte einlesen:

1
2
3
4
5
6
7
8
9
source conf.sh

# Einen Wert ändern: 
b=8

# geänderte Werte in Datei schreiben
echo "a=$a
b=$b
c=\"$c\""

Das source conf.sh können auch andere Files benutzen. Problem: Raceconditions. Wenn Script 1 den Wert schreibt weiß Script 2 nichts davon, außer über umständliche Mechanismen erreichbar. Andere Scripte sehen die neuen Werte erst, wenn sie erneut conf.sh einlesen.

Bei vielen Variablen wird es wahrscheinlich unübersichtlich.

Lesen andere Scripte die Werte ein, so muss man auch aufpassen, dass es nicht zu Namenskollisionen kommt, also Script 3 benutzt vielleicht die Variable "tmp" und Script 1 führt später auch ein *tmp* ein.

b) Um nicht alle Variablen einzeln beim Speichern aufführen zu müssen kann man ein assoziatives Array verwenden.

1
2
3
4
declare -A dict 
dict[a]=foo
dict[b]=7
dict[c]="/dev/cdrom" 

Die anderen Probleme (Namenskollision, Raceconditions) löst das natürlich nicht, aber der Code bleibt auch bei vielen Variablen knapp. Das Einlesen wird auch komplizierter als einfach source aufzurufen.

Siehe auch help source.

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 303

Wohnort: Aachen

Moin,

also Werte nach bestimmten Mustern in eine Datei schreiben und abrufen? - Da schreit für mich alles schon gerade zu nach einer JSON-Datei.

In Bash lässt sich diese ganz leicht mit jq erstellen und Bearbeiten. Zudem lässt sich das Format aber auch in anderen Sprachen easy verwalten.

Gruß Thomas

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13365

matze31 schrieb:

Ich habe name1=aaa, name2=bbb, name3=ccc u.s.w. Jetzt möchte ich aber den Wert name2 ändern von bbb auf ddd.

Meinst Du ein Array oder ein Dictionary? Letzteres hat typischerweise beliebige Schlüssel, während ein Array mit positiven Ganzzahlen (die starten meist bei 0) indiziert wird.

Die Datei muss auch für anderen Scripte erreichbar sein.

Meinst Du damit den physischen Zugriff des Lesens oder soll "erreichbar" auch bedeuten, dass die Datei ein Format hat, das womöglich von verschiedenen Skripten in verschiedenen Sprachen lesbar ist. Dann böte sich etwas wie Yaml oder JSON an. Man könnte auch Zeilen als Werte nehmen, also Newline als Trennzeichen.

Wenn man das rein in bash machen will, dann geht z.B.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!code bash

dict_file=~/.dict

dict_lese(){
  dict=()

  while read -r v; do
    dict+=("$v")
  done <"$dict_file"
}

dict_schreibe(){
  for v in "${dict[@]}"; do
    echo "$v"
  done >|"$dict_file"
}

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17672

Wohnort: Berlin

Wieso JSON?

Ich sehe Json als geeignetes Format um Konstrukte, wie sie für objektorientierte Sprachen typisch sind, an:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct Mitarbeiter {
    int: id
    String: name
}

struct Abteilung {
    int: id
    String: name
    Mitarbeiter[]: mitarbeiter
}

, wo man Verästelungen hat.

Wo ist der Vorteil gegenüber source, welches ein Buildin ist, so dass man sich keine unnötigen Abhängigkeiten ins Projekt zieht, und welches beim Lesen (vielleicht ist Schreiben für wenige/keine geplanten Anwendungen nötig?) trivial ist: Alle Variablen auf einen Rutsch, ein Einzeiler, eine bekannte Syntax für alle, die ohnehin Bash (oder ein ähnlicher Shelldialekt) lesen?

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1466

Da der TS sich etwas unklar ausgedrückt und Begriffe verwendet hat (z.B. dictionary), die eine definierte Bedeutung haben, ist es schwer zu erahnen, was er wirklich will.

Aus seinem Beispiel könnte man vermuten, das er eine Liste von Bash-Variablen mit deren Werten für mehrere Scripte zur Verfügung z.B. als Resource-Datei stellen will. Wenn diese meine Vermutung zutrifft, lässt sich das mit einer einfachen sequentiellen Datei realisieren, die das Script zu Beginn mit "source" einliest (siehe Beispiel von user_unknown).

Das Ändern der Werte geht über das komplette Neuschreiben der Datei (siehe user_unknown oder rklm als Array). Wenn man nur einzelne Werte ändern will geht dies auch z.B. mittels "sed". Beispiel:

# Erstelle Beispiel-Resource-Datei
RC=conf.sh;name1=aaa;name2=bbb;name3=ccc
# Sichere die Variablen name* in Datei $RC
set | grep ^name >$RC
# ---- nun fängt das Beispiel an
# definiere bash-Funktion zum Setzen/Ändern der Variable $1 auf den Wert $2
function mySet () { eval $1="$2"; grep ^$1= $RC &>/dev/null && sed -i "s/^$1=.*$/$1=${!1}/" $RC || set | grep ^$1= >>$RC ; }
# Ändere die Variable "name2" und füge Array "array4" hinzu
mySet name2 ddd
mySet array4 '( "eins" "zwei" )'
# Prüfe Ergebnis
set | grep -E "^(name|array).*="
cat $RC

Erläuterung der Funktion "mySet". Die Funktion erhält den Bash-Variablen-Namen ($1) und dessen Wert ($2). In der Funktion wird der Wert der Variablen zugewiesen. Wenn die Variable bereits in der Resource-Datei ($RC) definiert ist, wird die neue Zuweisung per "sed" in $RC aktualisiert, sonst per "set|grep)" an $RC angehängt. Variablen, die in $RC auftauchen sollen, sind daher über die Funktion "mySet VariablenName VariablenWert" zu setzen/pflegen.

Natürlich kann man statt einer einfachen Datei dies auch in einer .json mit ein paar kleinen Änderungen im "sed" oder "jq" abspeichern.

JS=conf.json
echo "[" >$JS
sed 's/"/\\"/g;s/\([^=]*\)=\(.*\)/{"\1": "\2"},/' $RC >>$JS
sed -zi 's/,\n$/\n]\n/' $JS
cat $JS | jq "."

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 303

Wohnort: Aachen

user_unknown schrieb:

Wieso JSON?

Ich sehe Json als geeignetes Format um Konstrukte, wie sie für objektorientierte Sprachen typisch sind, an

Das ist so nicht richtig (und dein Beispiel ist auch kein Json). Json ist schlichtweg ein sehr leicht lesbares Format um Mehrdimensionale Tabellen und Listen darzustellen.

Also eine Tabelle oder Liste, bei der jeder Zellwert/Listenpunkt wieder eine weitere Tabelle oder Liste sein kann (bis ins unendliche skalierbar).

Wo ist der Vorteil gegenüber source, welches ein Buildin ist

Wo soll ich da bloß anfangen? Neben bereits erwähnten...

  • Einfach nur Wörter in einem Wörterbuch zu deklarieren ist eine Sache - mit Json kannst du zu jedem Wort noch Beschreibungen hinzufügen, diese Kategorisieren, Alternativen hinzufügen, oder was auch immer einem gerade vorschwebt.

  • Das Format ist so leicht lesbar, dass es auch mit jedem gewöhnlichen Textprogramm editierbar ist

  • JQ bringt alles nötige mit, um Werte schnell einfach und sicher nach beliebigen Kriterien filtern oder ändern zu können

  • Das Format lässt sich jederzeit komplett umstrukturieren

  • Es gibt quasi für jede Skript-/Programmiersprache parser. Gerade an einem Windowsrechner und kein Linux zur Hand? - Kein Problem, bearbeitet man die Json halt ganz easy mit Powershell (nativ).

  • ...

matze31

(Themenstarter)

Anmeldungsdatum:
25. Oktober 2015

Beiträge: 836

Erstmal vielen dank für die schnellen und guten Antworten. Nachdem ich mir eine Übersicht gemacht habe würde ich json bevorzugen. Mit Json kann ich auch in Python arbeiten.

Was ist das einfachste in der bash eine solche Datei mit Einträgen zu erstellen, bzw viel mehr einen bestimmten Wert zu lesen und auch zu überschreiben? Es soll recht einfach gehalten werden. Nehmen wir mal an, die Datei sieht dann so aus:

 {"Name":"Max", "Alter":"20","Wohnort":"Berlin"} 

Ich möchte über das json iterieren und dann den Wert von den Schlüssel "Alter" ändern.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17672

Wohnort: Berlin

TK87 schrieb:

user_unknown schrieb:

Wieso JSON?

Ich sehe Json als geeignetes Format um Konstrukte, wie sie für objektorientierte Sprachen typisch sind, an

Das ist so nicht richtig (und dein Beispiel ist auch kein Json).

Mein Beispiel sollte auch kein Json sein, sondern Pseudocode, der geeignet ist, per Json dargestellt zu werden, weil er verästelt ist.

Json ist schlichtweg ein sehr leicht lesbares Format um Mehrdimensionale Tabellen und Listen darzustellen.

Nur das in der Frage weder von einer Tabelle, noch von Listen die Rede ist (und auch nicht von Bäumen).

Und eine Reihe einfacher Zuweisungen ist natürlich noch leichter zu lesen:

1
2
3
4
# conf.sh:
Name=Max
Alter=20
Wohnort=Berlin

Also eine Tabelle oder Liste, bei der jeder Zellwert/Listenpunkt wieder eine weitere Tabelle oder Liste sein kann (bis ins unendliche skalierbar).

YAGNI.

Wo ist der Vorteil gegenüber source, welches ein Buildin ist

Wo soll ich da bloß anfangen? Neben bereits erwähnten...

Beim ersten Vorteil, wenn es einen ersten Vorteil gibt.

  • Einfach nur Wörter in einem Wörterbuch zu deklarieren ist eine Sache - mit Json kannst du zu jedem Wort noch Beschreibungen hinzufügen,

War keine Anforderung.

diese Kategorisieren,

dito.

Alternativen hinzufügen,

dito.

oder was auch immer einem gerade vorschwebt.

Ach so.

  • Das Format ist so leicht lesbar, dass es auch mit jedem gewöhnlichen Textprogramm editierbar ist

Ist kein Vorteil, weil eine zeilenweise Wertzuweisung noch einfacher per Editor bearbeitbar ist.

  • JQ bringt alles nötige mit, um Werte schnell einfach und sicher nach beliebigen Kriterien filtern oder ändern zu können

Wie kann das einfacher sein, als zu schreiben "Alter=21"?

  • Das Format lässt sich jederzeit komplett umstrukturieren

YAGNI. Von einem Format war gar nicht die Rede.

  • Es gibt quasi für jede Skript-/Programmiersprache parser.

Für Schlüssel=Wert-Paare braucht man gar keinen Parser. Man spart sich also eine Abhängigkeit.

Gerade an einem Windowsrechner und kein Linux zur Hand? - Kein Problem, bearbeitet man die Json halt ganz easy mit Powershell (nativ).

Als ob Schlüssel-Wert-Paare unter Windows ein Problem wären. Bash war vorgegeben und unter Windows gibt es beispielsweise gitbash, falls man das doch auf Windows umziehen möchte. Auch da ist ein

1
source conf.sh

wohl kaum von irgendwelcher Jsongymnastik zu überholen.

juribel

Anmeldungsdatum:
20. April 2014

Beiträge: 1326

Schon mal jemand an SQLite gedacht? Damit gehts doch viel einfacher!

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 303

Wohnort: Aachen

matze31 schrieb:

Was ist das einfachste in der bash eine solche Datei mit Einträgen zu erstellen, bzw viel mehr einen bestimmten Wert zu lesen und auch zu überschreiben? Es soll recht einfach gehalten werden. Nehmen wir mal an, die Datei sieht dann so aus:

 {"Name":"Max", "Alter":"20","Wohnort":"Berlin"} 

Ich möchte über das json iterieren und dann den Wert von den Schlüssel "Alter" ändern.

Bei dem Beispiel einfach

1
2
3
json='{"Name":"Max", "Alter":"20","Wohnort":"Berlin"}'

jq 'select(.Name=="Max").Alter=21' <<<$json

Allerdings gibt es ja vermutlich mehrere Einträge, also dürfte wohl noch ein Array drum sein...

[{"Name":"Max", "Alter":20,"Wohnort":"Berlin"}]

Das sähe dann so aus:

1
jq '( .[] | select(.Name=="Max") ).Alter=21' <<<$json

Natürlich kann man auch nach mehreren Kriterien filtern...

1
jq '( .[] | select(.Name=="Max" and .Wohnort=="Frankfurt") ).Alter=21' <<<$json

TK87

Anmeldungsdatum:
8. Juli 2019

Beiträge: 303

Wohnort: Aachen

user_unknown schrieb:

Json ist schlichtweg ein sehr leicht lesbares Format um Mehrdimensionale Tabellen und Listen darzustellen.

Nur das in der Frage weder von einer Tabelle, noch von Listen die Rede ist (und auch nicht von Bäumen).

Wie shiro schon sagte:

Da der TS sich etwas unklar ausgedrückt und Begriffe verwendet hat (z.B. dictionary), die eine definierte Bedeutung haben, ist es schwer zu erahnen, was er wirklich will.

user_unknown schrieb:

Und eine Reihe einfacher Zuweisungen ist natürlich noch leichter zu lesen:

1
2
3
4
# conf.sh:
Name=Max
Alter=20
Wohnort=Berlin

Genau da wären wir beim entscheidenden Punkt: Du hast jetzt einen Adressbucheintrag definiert. Was machst du beim zweiten?

Und wenn du dann mehrere Einträge hast, wie filterst du nach Name, Wohnort, ..., um nur das Alter von Max zu ändern?

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1466

Schon mal jemand an SQLite gedacht? Damit gehts doch viel einfacher!

Yeah! Wenn man die einfache Lösung mit der flachen Datei nicht haben will sondern die komplexe Form einer .json Struktur bevorzugt, kann man auch SQLite3 verwenden.

Ab der Version 3.38.0 kann "sqlite3" ja auch nativ mit json Strukturen arbeiten. Dann kann man herrlich SQL Syntax mit den json_* Funktionen verweben.

juribel

Anmeldungsdatum:
20. April 2014

Beiträge: 1326

Echt jetzt? Das ist ja interessant!

Antworten |