Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Kommaseparierte Wörter auftrennen in Bash

4 views
Skip to first unread message

Marco Moock

unread,
May 9, 2023, 6:58:12 AM5/9/23
to
Hallo zusammen!

Ich will gerade eine durch Kommata separierte Liste auftrennen. Die
Ausgabe soll die Elemente samt einem Präfix und Suffix haben.

Wie kann man sowas in Bash sinnvoll bewerkstelligen?
Die Daten liegen als Datei vor.

Ich suche eine Skriptlösung (am liebsten Bash), da der Vorgang
in Zukunft mehrfach passieren wird.

Aus bla1,bla2,bla3 soll

abc bla1 xyz
abc bla2 xyz

werden, Präfix und suffix sind immer gleich.

Meine Idee war, die Datei in einer for-Schleife auszulesen und einen
Teil in eine lokale Variable zu speichern und dann direkt per echo den
Kram wie gewünscht auszugeben (das reicht völlig).

Doch wie grenze ich den Bereich ein, sodass für jedes Komma die
Schleife durchlaufen wird?

--
Gruß
Marco

Helmut Waitzmann

unread,
May 9, 2023, 8:41:54 AM5/9/23
to
Marco Moock <mo...@posteo.de>:
> Ich will gerade eine durch Kommata separierte Liste auftrennen.
> Die Ausgabe soll die Elemente samt einem Präfix und Suffix
> haben.
>
> Wie kann man sowas in Bash sinnvoll bewerkstelligen?
>

(Das unten gezeigte funktioniert sogar in jedem zu POSIX
kompatiblen Shell.)

> Die Daten liegen als Datei vor.
>
>
> Ich suche eine Skriptlösung (am liebsten Bash), da der Vorgang
> in Zukunft mehrfach passieren wird.
>
> Aus bla1,bla2,bla3 soll
>
> abc bla1 xyz
> abc bla2 xyz
>
> werden, Präfix und suffix sind immer gleich.
>
>
> Meine Idee war, die Datei in einer for-Schleife auszulesen und einen
> Teil in eine lokale Variable zu speichern und dann direkt per echo den
> Kram wie gewünscht auszugeben (das reicht völlig).
>
> Doch wie grenze ich den Bereich ein, sodass für jedes Komma die
> Schleife durchlaufen wird?
>

Man braucht dafür keine Schleife, wenn man die Fähigkeit des
Shells, bei Variablenexpansionen ohne Anführungszeichen die
Variablenwerte an den in der Variablen «IFS» aufgezählten Zeichen
zu zersägen, nutzt:

liste='bla1,bla2,bla3'
# Die Shell-Option f merken und abschalten:
case "$-" in
(*f*) opt_f_set=:
;;
(*) opt_f_set=false
;;
esac &&
set +f &&
# Die Variable IFS merken und setzen:
saved_IFS="${IFS-}" && ${IFS+:} unset saved_IFS && IFS=','
set -- $liste &&
# Die Variable IFS wiederherstellen, wie sie war:
IFS="${saved_IFS-}" && ${saved_IFS+:} unset IFS &&
# Jetzt enthaelt "$@" die Reihe der Elemente, jedes in einem
# positional parameter.
# Die Shell-Option f wiederherstellen, wie sie war:
if "$opt_f_set" ; then set -f ; fi &&
# Das Gewuenschte ausgeben:
printf 'abc %s xyz\n' "$@"

Falls Präfix und Suffix nicht vor dem Schreiben der Kommandozeile
schon feststehen, muss man zum Ausgeben eine Schleife nehmen,
weil das Hineindrücken in den «printf»‐Formatierungsparameter
nicht ohne Einsatz des eigenen Gehirns (Beispielsweise machen
Prozentzeichen Ärger!) geht:

for element
do
printf '%s%s%s\n' "$praefix" "$element" "$suffix"
done

Marco Moock

unread,
May 9, 2023, 8:53:07 AM5/9/23
to
Am 09.05.2023 um 12:58:04 Uhr schrieb Marco Moock:

> Ich will gerade eine durch Kommata separierte Liste auftrennen. Die
> Ausgabe soll die Elemente samt einem Präfix und Suffix haben.
>
> Wie kann man sowas in Bash sinnvoll bewerkstelligen?
> Die Daten liegen als Datei vor.
>
> Ich suche eine Skriptlösung (am liebsten Bash), da der Vorgang
> in Zukunft mehrfach passieren wird.

Ich bekam per Mail eine solche:

#!/bin/bash
for x in $(cat /tmp/subscriber|tr ',' '\n') ; do echo "Präfix "$x"
Suffix"
done

Danke an alle für die Antworten.

Michael Uplawski

unread,
May 9, 2023, 9:16:49 AM5/9/23
to
Holdrio.


> Falls Präfix und Suffix nicht vor dem Schreiben der Kommandozeile schon
> feststehen, muss man zum Ausgeben eine Schleife nehmen, weil das
> Hineindrücken in den «printf»‐Formatierungsparameter nicht ohne Einsatz
> des eigenen Gehirns (Beispielsweise machen Prozentzeichen Ärger!) geht:
>
> for element
> do
> printf '%s%s%s\n' "$praefix" "$element" "$suffix"
> done

*Wenn* man diese Schleife akzeptiert, geht das vorherige Ersetzen der
Kommata aber auch einfacher:

dolf@gym:~$ tr ',' '\n' < /tmp/ding.txt > /tmp/neu.txt

Die „elements” kommen danach direkt aus neu.txt oder was man so hat.

Cheerio.
--
Es ist an der Zeit

Christian Garbs

unread,
May 9, 2023, 2:28:42 PM5/9/23
to
Mahlzeit!

Marco Moock <mo...@posteo.de> wrote:

> #!/bin/bash
> for x in $(cat /tmp/subscriber|tr ',' '\n') ; do echo "Präfix "$x"
> Suffix"
> done

mögliche Verbesserungen:

* Useless use of cat: tr kann selber Dateien lesen, dafür bracht es
das cat nicht:
… in $(tr ',' '\n' < /tmp/subscriber) ; …

* Warum ist ausgerechnet das $x nicht in den doppelten
Anführungszeichen des echo enthalten? Das irritiert mich.

* Bei größeren Dateien kann die Kommandozeile zu lang werden, wenn
das $() evaluiert wird. Alternative:

tr ',' '\n' < /tmp/subscriber | \
while read -r x; do echo "Präfix $x Suffix"; done



Wenn Du sowieso mit externen Kommandos wie tr arbeitest, kannst Du das
vermutlich auch komplett in awk oder sed lösen, dann ist sogar egal,
welche Shell Du verwendest ;-)

Gruß
Christian
--
....Christian.Garbs....................................https://www.cgarbs.de
Java-Klassen fangen immer mit denselben 4 Byte an:
0xCA 0xFE 0xBA 0xBE => CAFE BABE (F. Paetz)

Felix Palmen

unread,
May 9, 2023, 2:49:22 PM5/9/23
to
* Christian Garbs <mi...@cgarbs.de>:
> tr ',' '\n' < /tmp/subscriber | \
> while read -r x; do echo "Präfix $x Suffix"; done
>
> Wenn Du sowieso mit externen Kommandos wie tr arbeitest, kannst Du das
> vermutlich auch komplett in awk oder sed lösen, dann ist sogar egal,
> welche Shell Du verwendest ;-)

Ist es auch so? Also ich sehe da nichts, was nicht POSIX/bourne shell
syntax entsprechen würde. Daher, direkt den shebang auf #!/bin/sh
ändern, um eine unnötige Abhängigkeit zu einer konkreten bourne shell zu
vermeiden :)

--
Dipl.-Inform. Felix Palmen <fe...@palmen-it.de> ,.//..........
{web} http://palmen-it.de {jabber} [see email] ,//palmen-it.de
{pgp public key} http://palmen-it.de/pub.txt // """""""""""
{pgp fingerprint} 6936 13D5 5BBF 4837 B212 3ACC 54AD E006 9879 F231

Helmut Waitzmann

unread,
May 9, 2023, 3:56:54 PM5/9/23
to
Michael Uplawski <michael....@uplawski.eu>:
>> Falls Präfix und Suffix nicht vor dem Schreiben der
>> Kommandozeile schon feststehen, muss man zum Ausgeben eine
>> Schleife nehmen, weil das Hineindrücken in den
>> «printf»‐Formatierungsparameter nicht ohne Einsatz des eigenen
>> Gehirns (Beispielsweise machen Prozentzeichen Ärger!) geht:
>>
>> for element
>> do
>> printf '%s%s%s\n' "$praefix" "$element" "$suffix"
>> done
>
> *Wenn* man diese Schleife akzeptiert, geht das vorherige
> Ersetzen der Kommata aber auch einfacher:
>
> dolf@gym:~$ tr ',' '\n' < /tmp/ding.txt > /tmp/neu.txt

Das ist nicht einfacher, sondern komplizierter, weil man sich
dann darum kümmern muss, woher man einen Dateinamen für die
temporäre Datei herbekommt.

Stell dir nur mal vor, das Kommando läuft gleichzeitig mehr als
ein Mal.

Außerdem entspricht es nicht ganz der Aufgabenstellung:  Die
Elemente sollten mit je einem Komma getrennt sein.  Das bedeutet,
dass innerhalb eines Elements kein Komma vorkommt.  Dass sie auch
mit Zeilenwechseln getrennt sein können, war nicht zugesichert. 
Das bedeutet, dass innerhalb eines Elements auch Zeilenwechsel
vorkommen dürfen.

Beispiel:

nl="$( printf '%b.' '\n' )" && nl="${nl%?}" &&
liste=eins"$nl"1,zwei"$nl"2,drei"$nl"3 &&
printf '%s\n' "$liste" | tr -- , '\n'

erzeugt sechs statt drei Elemente im Gegensatz zur
Aufgabenstellung.

Michael Uplawski

unread,
May 9, 2023, 4:14:28 PM5/9/23
to

> Die Elemente
> sollten mit je einem Komma getrennt sein.  Das bedeutet, dass innerhalb
> eines Elements kein Komma vorkommt.  Dass sie auch mit Zeilenwechseln
> getrennt sein können, war nicht zugesichert.  Das bedeutet, dass
> innerhalb eines Elements auch Zeilenwechsel vorkommen dürfen.

Ich bin mit allem, was du schreibst einverstanden, hielt es aber auch
nicht für notwendig, Details in die Spezifikation einzubauen, weil Marco
das bisher unterlassen hat.

Mir ist, als brauche Marco Ideen; keine fertige Lösung. Vielleicht käme
der Kommentar zu den Zeilenumbrüchen dann von ihm selber.., was ich
vorzöge. ;)

Christian Garbs

unread,
May 9, 2023, 5:09:37 PM5/9/23
to
Mahlzeit!

Felix Palmen <fe...@palmen-it.de> wrote:

> * Christian Garbs <mi...@cgarbs.de>:
>> tr ',' '\n' < /tmp/subscriber | \
>> while read -r x; do echo "Präfix $x Suffix"; done
>>
>> Wenn Du sowieso mit externen Kommandos wie tr arbeitest, kannst Du das
>> vermutlich auch komplett in awk oder sed lösen, dann ist sogar egal,
>> welche Shell Du verwendest ;-)
>
> Ist es auch so? Also ich sehe da nichts, was nicht POSIX/bourne shell
> syntax entsprechen würde. Daher, direkt den shebang auf #!/bin/sh
> ändern, um eine unnötige Abhängigkeit zu einer konkreten bourne shell zu
> vermeiden :)

Das stimmt - ich hatte das anders gemeint:

Der Sprung von tr auf sed/awk ist nicht weit. Und wenn er das mit
denen löst, läuft es dann auch unter zsh, CMD.EXE oder PowerShell ;-)

Grüße
Christian


PS: Gerade durch "selber Nachgucken" gelernt: \n in tr ist sogar
POSIX-konform. Ich dachte, das wäre eine GNU-Extension oder sowas.
Mit welchem anderen Tool könnte ich das verwechselt haben?
--
....Christian.Garbs....................................https://www.cgarbs.de
Drücke ich während der Fahrt das mittlere Pedal in meinem Wagen, kann ich
jederzeit vor Kreuzungen oder Ortschaften die Geschwindigkeit drosseln.

Christian Weisgerber

unread,
May 9, 2023, 5:30:14 PM5/9/23
to
On 2023-05-09, Christian Garbs <mi...@cgarbs.de> wrote:

>> for x in $(cat /tmp/subscriber|tr ',' '\n') ; do echo "Präfix "$x"
>> Suffix"
>
> * Bei größeren Dateien kann die Kommandozeile zu lang werden, wenn
> das $() evaluiert wird.

Die Wortliste einer for-Schleife ist keine Befehlszeile. Ihre Länge
ist nicht durch ARG_MAX limitiert, sondern nur durch den allozierbaren
Speicher.

$ cd /usr/src
$ ls $(find . -print)
-sh: /bin/ls: Argument list too long
$ for x in $(find . -print); do echo "$x"; done | head
.
./.got
./.got/lock
./.got/file-index
./.got/base-commit
./.got/head-ref
./.got/repository
./.got/path-prefix
./.got/uuid
./.got/format

--
Christian "naddy" Weisgerber na...@mips.inka.de

Ulli Horlacher

unread,
May 10, 2023, 1:44:51 AM5/10/23
to
Martin Schnitkemper <news.trash...@spamgourmet.com> wrote:
> Am Di, 09.05.2023 um 21:56 schrieb Helmut Waitzmann:
>
>> Das ist nicht einfacher, sondern komplizierter, weil man sich
>> dann darum kümmern muss, woher man einen Dateinamen für die
>> temporäre Datei herbekommt.
>
> mktemp

Das raeumt dann aber die nicht mehr benoetigte tmp-Datei danach nicht mehr
weg. Bei Fehlerabbruch bleibt die dann liegen.
Mit tmp-Dateien zu arbeiten ist idR Pfusch.

--
Ullrich Horlacher Server und Virtualisierung
Rechenzentrum TIK
Universitaet Stuttgart E-Mail: horl...@tik.uni-stuttgart.de
Allmandring 30a Tel: ++49-711-68565868
70569 Stuttgart (Germany) WWW: https://www.tik.uni-stuttgart.de/

Ulli Horlacher

unread,
May 10, 2023, 2:23:38 AM5/10/23
to
Christian Weisgerber <na...@mips.inka.de> wrote:

> Die Wortliste einer for-Schleife ist keine Befehlszeile. Ihre Länge
> ist nicht durch ARG_MAX limitiert, sondern nur durch den allozierbaren
> Speicher.
>
> $ cd /usr/src
> $ ls $(find . -print)
> -sh: /bin/ls: Argument list too long
> $ for x in $(find . -print); do echo "$x"; done | head
> .
> ./.got
> ./.got/lock

Leider ist das oft kein brauchbarer Ersatz.
Das korrekte 2. Beispiel ist:

$ for x in $(find . -print); do ls "$x"; done

Und das startet fuer jede gefundene Datei einen Prozess, waehrend im
ersten Beispiel es insgesamt nur 2 sind.

Also muss man tricksen:

$ find . -print -exec ls {} +

Wobei ich mir nicht sicher bin, ob das POSIX konform ist oder GNU
Schpaeschl.

Felix Palmen

unread,
May 10, 2023, 3:34:04 AM5/10/23
to
* Christian Garbs <mi...@cgarbs.de>:
> Das stimmt - ich hatte das anders gemeint:
>
> Der Sprung von tr auf sed/awk ist nicht weit. Und wenn er das mit
> denen löst, läuft es dann auch unter zsh, CMD.EXE oder PowerShell ;-)

Zsh ist ja ebenfalls POSIX-konform. Aber man könnte noch seltsame Dinge
wie csh, fish, ... ergänzen ;)

> PS: Gerade durch "selber Nachgucken" gelernt: \n in tr ist sogar
> POSIX-konform. Ich dachte, das wäre eine GNU-Extension oder sowas.
> Mit welchem anderen Tool könnte ich das verwechselt haben?

Eventuell perl-style character classes in regex, wie z.B. GNU sed es
versteht, also z.B. \s statt [[:space:]]? Das ist leider ein häufig
anzutreffendes portability issue ...

Allerdings zu tr, das ist sowieso kein builtin (weder in POSIX noch in
der bash), von daher würde eine andere Shell sowieso auch nicht
weiterhelfen.

Grüße, Felix

Michael Uplawski

unread,
May 10, 2023, 4:52:39 AM5/10/23
to
Ich meine, Marco hat die Möglichkeit, ein technisches Problem (wie komme
ich darauf) dank eurer Unterstützung auszuräumen.

Der Rest ist Optimierug und – freilich – Prophylaxe gegen dies und jenes.

Martin Schnitkemper hat geschrieben:

> Am Mi, 10.05.2023 um 05:43 schrieb Ulli Horlacher:
>
> > Das raeumt dann aber die nicht mehr benoetigte tmp-Datei danach nicht mehr
> > weg.
>
> Die Datei liegt auf einem tmpfs und ist spätestens nach dem nächsten reboot
> wieder verschwunden. Wer sie eher räumen möchte, kann das in seinem Skript
> erledigen.

Zum Beispiel gehe ich mutig davon aus, das sowas naheliegt und eventuell
sogar die Regel ist.

> > Bei Fehlerabbruch bleibt die dann liegen.
>
> Wenn die Verarbeitung nach einem Abbruch neu gestartet wird, bekommt man
> auch wieder eine neue Datei, so dass es auch dann zu keinen Konflikten
> kommt. In sofern kann ich die Bedenken nicht nachvollziehen.

Hier geht es um den Fakt, dass Platz von einer obsoleten Datei
“verschwendet” wird.

> > Mit tmp-Dateien zu arbeiten ist idR Pfusch.
>
> Es ging nicht um den Sinn oder Unsinn von temporären Dateien sondern
> darum, woher man einen Dateinamen für eine temporäre Datei bekommt. Und
> genau das leistet 'mktemp'.

Es ging sogar nur darum, eine Liste umzuorganisieren.

> Ich kann auch nicht erkennen, warum es Pfusch sein soll, wenn man Dateien
> anlegt, die man während einer Verarbeitung nur vorübergehend benötigt und
> danach wieder verwirft.

Den Happiness-Quotienten auszurechnen, wenn nur einfach die profanste
aller Skriptlösungen brauchbare Ergenisse liefert, würde ich euch ja
auch überlassen.

Unterm Strich glaube ich, dass der Thread jetzt einen neuen Betreff
verdient hätte.

Cheerio

Christian Weisgerber

unread,
May 10, 2023, 5:32:00 AM5/10/23
to
On 2023-05-10, Ulli Horlacher <fram...@rus.uni-stuttgart.de> wrote:

>> mktemp
>
> Das raeumt dann aber die nicht mehr benoetigte tmp-Datei danach nicht mehr
> weg. Bei Fehlerabbruch bleibt die dann liegen.

Daher in Shell-Skripten sehr beliebt:

trap "rm -f $tmpfile" EXIT HUP INT TERM # oder 0 1 2 15

Ulli Horlacher

unread,
May 10, 2023, 6:27:40 AM5/10/23
to
Martin Schnitkemper <news.trash...@spamgourmet.com> wrote:
> Am Mi, 10.05.2023 um 05:43 schrieb Ulli Horlacher:
>
>> Das raeumt dann aber die nicht mehr benoetigte tmp-Datei danach nicht mehr
>> weg.
>
> Die Datei liegt auf einem tmpfs

Nur, wenn du ein tmpfs eingerichtet hast und as mktemp angegeben hast.


> und ist spätestens nach dem nächsten reboot wieder verschwunden.

Der erst nach Wochen passiert. Bis dahin sammeln sich lustig hunderte
tmp-files gammlig an.


> Wer sie eher räumen möchte, kann das in seinem Skript erledigen.

Beachtest du da alle Signale und Fehlerbehandlung?
Das ist NICHT trivial.


> Wenn die Verarbeitung nach einem Abbruch neu gestartet wird, bekommt man
> auch wieder eine neue Datei, so dass es auch dann zu keinen Konflikten
> kommt. In sofern kann ich die Bedenken nicht nachvollziehen.

Es bleibt Muell zurueck, der nervt.


> Ich kann auch nicht erkennen, warum es Pfusch sein soll, wenn man Dateien
> anlegt, die man während einer Verarbeitung nur vorübergehend benötigt und
> danach wieder verwirft.

Weil es (besser) auch ohne geht, meistens.

Helmut Waitzmann

unread,
May 10, 2023, 1:44:14 PM5/10/23
to
Ulli Horlacher <fram...@rus.uni-stuttgart.de>:
> Martin Schnitkemper <news.trash...@spamgourmet.com> wrote:
>> Am Di, 09.05.2023 um 21:56 schrieb Helmut Waitzmann:
>>
>>> Das ist nicht einfacher, sondern komplizierter, weil man sich
>>> dann darum kümmern muss, woher man einen Dateinamen für die
>>> temporäre Datei herbekommt.
>>
>> mktemp
>
> Das raeumt dann aber die nicht mehr benoetigte tmp-Datei danach
> nicht mehr weg. Bei Fehlerabbruch bleibt die dann liegen.
> Mit tmp-Dateien zu arbeiten ist idR Pfusch.
>

Wenn man es richtig macht, bleibt sie bei Fehlerabbruch
(höchstwahrscheinlich) nicht liegen:  Man öffnet die Datei zum
Schreiben und zum Lesen und löscht sie, beispielsweise
mit den folgenden Bash‐kommandos:

tmpDatei="$( mktemp )" &&
exec {tmpDateiFD}<> "$tmpDatei" &&
rm -- "$tmpDatei"

und beginnt erst danach damit, den file descriptor, dessen Nummer
in der Bash‐Variablen «tmpDateiFD» vermerkt ist, zu verwenden. 
Zu diesem Zeitpunkt hat die Datei bereits keinen Namen mehr und
verschwindet daher automatisch aus dem Dateisystem, sobald es
keinen Prozess mehr gibt, der sie geöffnet hat.  => Sollte der
Prozess zu diesem oder einem späteren Zeitpunkt an einem Fehler
sterben, bleibt die Datei nicht übrig.

Christian Garbs

unread,
May 10, 2023, 4:15:52 PM5/10/23
to
Mahlzeit!

Christian Weisgerber <na...@mips.inka.de> wrote:
> On 2023-05-09, Christian Garbs <mi...@cgarbs.de> wrote:
>
>>> for x in $(cat /tmp/subscriber|tr ',' '\n') ; do echo "Präfix "$x"
>>> Suffix"
>>
>> * Bei größeren Dateien kann die Kommandozeile zu lang werden, wenn
>> das $() evaluiert wird.
>
> Die Wortliste einer for-Schleife ist keine Befehlszeile. Ihre Länge
> ist nicht durch ARG_MAX limitiert, sondern nur durch den allozierbaren
> Speicher.

Ich hab noch kurz überlegt an der Stelle, leider fahsclrum.

Danke!
Christian
--
....Christian.Garbs....................................https://www.cgarbs.de
We can predict everything except the future.
0 new messages