ubuntuusers.de

Bash, sed etc: Wie "Paare" tauschen

Status: Gelöst | Ubuntu-Version: Xubuntu 22.04 (Jammy Jellyfish)
Antworten |

glaskugel

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3710

Es gibt eine Variable, die sieht so aus:

var="bla/blu was/das"

in der Praxis größer und es kommen auch Sonderzeichen wie @ oder Punkt vor.

Ergebnis soll sein:

var="blu/bla das/was"

Trenner eines Paares ist also das Leerzeichen

Das 2. Element des Paares soll also mit dem 1. Element getauscht werden.

Nun könnte man das in eine for-Schleife packen und dann mit cut tauschen. Aber ist das nicht schöner / eleganter lösbar, zB mit sed?

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9704

Wohnort: Münster

Es geht in der Bash mit simpler Zeichenkettenbearbeitung:

$ var="bla/blu was/das"
$ echo "${var#* } ${var% *}"
was/das bla/blu

Siehe: Shell/Bash-Skripting-Guide für Anfänger (Abschnitt „Zeichenketten-bearbeiten“)

TausB

Avatar von TausB

Anmeldungsdatum:
26. November 2009

Beiträge: 1570

Wohnort: Terra incognita

... das hat er nicht gemeint - gucke mal genau auf sein Beispiel ... 😉

wxpte

Anmeldungsdatum:
20. Januar 2007

Beiträge: 1388

Ein Ansatz:

1
sed 's#\([[:alpha:]]\+\)/\([[:alpha:]]\+\) \([[:alpha:]]\+\)/\([[:alpha:]]\+\)#\2/\1 \4/\3#' <<< "$var"

glaskugel schrieb:

in der Praxis größer und es kommen auch Sonderzeichen wie @ oder Punkt vor.

Dann muss die Regex entsprechend erweitert werden:

[[:alpha:]@.]

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1270

geht auch so:

$ var="bla/blu was/das eins/zwei drei/vier /fünf sechs/ sieben/acht"
$ neu=$(sed -E 's#([^/]*)/([^ ]*) *#\2/\1 #g' <<<$var)
$ echo $neu
blu/bla das/was zwei/eins vier/drei fünf/ /sechs acht/sieben
$

wxpte

Anmeldungsdatum:
20. Januar 2007

Beiträge: 1388

shiro schrieb:

geht auch so:

Sogar viel besser. 👍

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Man könnte auch Perl nehmen und sagen, dass er einfach auf alles schauen soll, was kein Whitespace ist - das hätte den Vorteil, dass es egal ist, wie viele Paare in der Variable vorkommen:

$ var="bla/blu was/das"
$ perl -pe 's#(\S+)/(\S+)#\2/\1#g' <<< "$var"
blu/bla das/was 

kB Team-Icon

Supporter, Wikiteam
Avatar von kB

Anmeldungsdatum:
4. Oktober 2007

Beiträge: 9704

Wohnort: Münster

TausB schrieb:

... das hat er nicht gemeint - gucke mal genau auf sein Beispiel

Ja, Du hast recht. Aber die Lösung geht genauso wie von mir beschrieben. Natürlich muss man über jedes Paar iterieren und statt des Leerzeichens den Schrägstrich als Trenner der beiden Teile nehmen:

var="bla/blu was/das"
for Paar in $var ; do echo -n "${Paar#*/}/${Paar%/*} " ; done
echo

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13204

shiro schrieb: [...]

Meine Lösung ist ähnlich. Allerdings schließe ich Leerzeichen aus, weil die als Trenner bezeichnet wurden.

1
2
3
$ var="blu/bla das/was"
$ echo "$var" | sed -r 's#([^/ ]+)/([^/ ]+)#\2/\1#g'
bla/blu was/das

Generell frage ich mich allerdings, was das für ein Anwendungsfall ist, der so eine merkwürdige Datenstruktur in einer einzelnen Shell-Variable erfordert.

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3710

Generell frage ich mich allerdings, was das für ein Anwendungsfall ist, der so eine merkwürdige Datenstruktur in einer einzelnen Shell-Variable erfordert.

Schwierig zu erklären.

Die Struktur habe ich so festgelegt.

Ich will da jetzt nicht viel darüber diskutieren. Es gibt mehrere Email-Adressen, wobei immer nur an die gleiche Domain ein Mail verschickt werden soll. Die Datenbasis ist auch wieder eine komplizierte Analyse Damit gibt es localparts und Domains von der Email-Adresse Nun geht es darum 2 Listen zu erstellen: Sortiert nach User Empfänger Email-Adresse - Absenderadresse Absenderadresse / Empfänger-Emailadresse Dazu gibt es noch Bedingungen auf die ich jetzt nicht eingehe

Der springede Punkt ist wie die Datenbasis erstellt wurde. Aber letztlich alles egal, ich bin fast am Ziel.

Mit " " als Trenner passt das gut für eine for-Schleife.

Es gibt 2 Elemente, die habe ich mit / getrennt, weil der / sicher nicht vorkommt.

Nun brauche ich verschiedene Darstellungsweisen, ich kann es drehen wie ich will.

"bla/blu was/das" ist das Ergebnis, das funktioniert und über komplizierte Analysen erreicht wird. Es bringt nichts das schon am Anfang zu ändern. Es ist egal was ich drehe, nur so wie es ist, funktioniert es ja schon. Also drehe ich zum Schluss die Darstellung und fertig.

Zur Zeit sind es ca. 70 durch Leerzeichen getrennte Teile, also nicht so viel um den PC ins Schwitzen zu bekommen.

Ich arbeite noch an was anderem und probiere das aus, sobald ich mit dem anderen fertig bin. Danke!

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13204

glaskugel schrieb:

Es gibt mehrere Email-Adressen, wobei immer nur an die gleiche Domain ein Mail verschickt werden soll.

Es bleibt für mich rätselhaft. Ich habe aber den Eindruck, dass Dein Problem mit einer vollwertigen Skriptsprache wie Python oder Ruby, die es erlauben Datentypen zu definieren, besser gelöst würde.

Gut, wenn Du für Dich eine Lösung gefunden hast, die funktioniert.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17620

Wohnort: Berlin

Meine Lösung wäre auch ähnlich zu denen von shiro und rklm ...

1
echo  "eins/zwei one/two una/duo 1/2" | sed -r "s|([^/]+)/([^ ]+)[ $]|\2/\1 |g"

... und käme auch mit mehr als 2 Paaren zurecht.

Am einfachsten zu verstehen, wenn auch länger im Code und ebenfalls mit externer Abhängigkeit (awk) ist wohl:

1
2
3
4
5
6
7
8
9
var="eins/zwei one/two" 
for paar in $var
do
    a=$(echo -n $paar | col1 "/")
    b=$(echo -n $paar | col2 "/")
    echo -n $b/$a" "
done 
echo 
# zwei/eins two/one 

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Man könnte auch read einspannen, um beide Teile des Paars in einem Rutsch zu bekommen:

1
2
3
4
5
6
7
8
var="eins/zwei one/two" 
for paar in $var
do
    IFS='/' read -r a b <<< "$paar";
    echo "$b/$a"
done | paste -sd ' '
echo 
# zwei/eins two/one 

glaskugel

(Themenstarter)

Anmeldungsdatum:
8. Juli 2010

Beiträge: 3710

Danke, die Lösung mit sed von rkim gefällt mir am besten. Ich wollte eine Schleife vermeiden. Meine Idee war ursprünglich cut mit for zu vermeiden.

dass Dein Problem mit einer vollwertigen Skriptsprache wie Python oder Ruby, die es erlauben Datentypen zu definieren, besser gelöst würde.

Mag schon sein, aber ich will nicht mit Kanonen auf Spatzen schießen, passt schon für diese Kleinigkeit.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17620

Wohnort: Berlin

Man kann auch einfach / und Blank als IFS definieren und mit 4 Variablen arbeiten:

1
2
3
4
#!/bin/bash
var="eins/zwei one/two"
IFS="[ /]" read p11 p12 p21 p22 <<< $var
echo "res:> $p12 $p11 $p22 $p21"

Mit read -d kann man offenbar nur ein Zeichen als Delimiter deklarieren.

Man kann natürlich auch erst mal Oracle installieren, via Docker, in der Cloud …

Antworten |