ubuntuuser1337
Anmeldungsdatum: 21. Juli 2015
Beiträge: Zähle...
|
Hallo zusammen, folgender Input liegt (beispielhaft) in einer Datei vor: abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ... ich möchte zwischen |1| und |2| b und d durch etwas anderes ersetzen. Mit einer einfachen sed-Anweisung komme ich nicht weiter:
| sed 's/|1|(.*)|2|/|1|\1|2|\n/g'
|
Ist es möglich verschachtelt zu ersetzen? Oder ist sed nicht der richtige Ansatz? Danke im Voraus für die Hilfe
|
frostschutz
Anmeldungsdatum: 18. November 2010
Beiträge: 7657
|
Mit sed ist das nicht ganz einfach. Und wenn "etwas anderes" dann womöglich auch wieder b oder d enthält, wirds richtig fies. line='abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...'
line=$(
IFS='|'
set $line
while [ ${#@} -gt 0 ]
do
echo -n "$1"
if [ "$1" == "1" -a "$3" == "2" ]
then
echo -n "|$2" | sed -e s/b/etwas/g -e s/d/anderes/g
shift
fi
shift
[ ${#@} -gt 0 ] && echo -n "|" && continue
break
done
)
echo "$line"
abcdabc|1|aetwascanderesaetwasc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|etwasacanderesetwasac|2|abcdabc|3|abcdabc|4|abcdabc ... Das ist jetzt Bash, vielleicht kann awk das intern auch anstellen mit Feldern. sed kennt nur pattern space und hold buffer, damit kann man das zwar auch hintricksen, aber nicht wirklich schön.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Wenn alles in einer Zeile steht, dann kannst Du um die |n| Zeilenvorschübe bauen, sodann die Zeilen als Bereiche definieren, innerhalb derer ersetzt werden soll, und dann zurückändern:
| echo "abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc .." | tr "|" "\n" | sed -r '/^1$/,/^2$/s/[bd]/ etw.and. /g' | tr "\n" "|"
abcdabc|1|a etw.and. c etw.and. a etw.and. c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1| etw.and. ac etw.and. etw.and. ac|2|abcdabc|3|abcdabc|4|abcdabc ..|
|
Wenn es doch Zeilen gibt, aber ein anderes Zeichen nicht, etwa '#', dann kannst Du vorab die Newlines in # ändern und am Schluss die Newlines umgekehrt rekonstruieren.
|
frostschutz
Anmeldungsdatum: 18. November 2010
Beiträge: 7657
|
Ooh. Schöne Lösung 😀
dann kannst Du vorab die Newlines in # ändern
Ab einer gewissen Dateigröße wird das unschön. Aber man kann das schon in einem sed Programm verwurschteln (eine Zeile laden, diese in mehrere Zeile verwandeln und wieder zurück, dann nächste echte Zeile...). Zumindest wenn das /^1$/,/^2$/ auch im pattern space funktioniert, muss man mal ausprobieren...
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Hi ubuntuuser1337, zuerst mal herzlich willkommen hier auf dem Forum !° Was Du vorhast, geht geht mit sed durchaus, nur der Code liest sich nicht besonders intuitiv ... 😀 Für Dein Beispiel wäre das ± sowas: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' | sed 's/\([^|]|[^|]|[^b|]*\)b/\1-was-/; s/\([^|]|[^|]|[^d|]*\)d/\1-anderes-/'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ... Es funktioniert, aber nach 1/2 Jahr verstehst Du den Code selber nicht mehr. Nicht kürzer, aber wesentlich übersichtlicher sähe das mit awk aus, weil das grundsätzlich nach "Worten" strukturiert ist: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' | awk -F '|' '{ OFS=FS; sub("b", "-was-", $3); sub("d", "-anderes-", $3); print }'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ... - Edit: Oder habe ich Dich falsch verstanden, und Du möchtest die "b" und "d" in der Zeile auch mehrfach ersetzen, falls sie zwischen "|1|" und "|2|" stehen ? - das ginge in der Tat einigermaßen vernünftig mit sed zu machen. Ok, besonders übersichtlich ist es auch nicht, aber es geht: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' | sed 's/\(|1|[^b|]*\)b\([^|]*|2|\)/\1-was-\2/g; s/\(|1|[^d|]*\)d\([^|]*|2|\)/\1-anderes-\2/g'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes-bac|2|abcdabc|3|abcdabc|4|abcdabc ... Das selbe geht natürlich auch mit awk, nur musst Du dann mit einer Schleife die "Worte" abklappern: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' | awk -F '|' '{ OFS=FS; while(++i < NF) if($i == 1) { sub("b", "-was-", $(i+1)); sub("d", "-anderes-", $(i+1)) } print }'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes-bac|2|abcdabc|3|abcdabc|4|abcdabc ... LG, track
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
Ich finde ja eine Lösung mit Ruby recht elegant... $ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc' \
| ruby -pe '$_[/\|1\|.*?\|2\|/]=$_[/\|1\|.*?\|2\|/].gsub(/[bd]/, "<was anderes>")'
abcdabc|1|a<was anderes>c<was anderes>a<was anderes>c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc Wenn Du "b" und "d" jeweils durch eigenen Text ersetzen willst, wird es etwas komplizierter: $ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc' \
| ruby -pe '$_[/\|1\|.*?\|2\|/]=$_[/\|1\|.*?\|2\|/].gsub(/[bd]/) {|m| m == "b" ? "<BBB>" : "<DDD>"}'
abcdabc|1|a<BBB>c<DDD>a<BBB>c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ☺
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12822
|
track schrieb:
Edit: Oder habe ich Dich falsch verstanden, und Du möchtest die "b" und "d" in der Zeile auch mehrfach ersetzen, falls sie zwischen "|1|" und "|2|" stehen ? - das ginge in der Tat einigermaßen vernünftig mit sed zu machen. Ok, besonders übersichtlich ist es auch nicht, aber es geht: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' | sed 's/\(|1|[^b|]*\)b\([^|]*|2|\)/\1-was-\2/g; s/\(|1|[^d|]*\)d\([^|]*|2|\)/\1-anderes-\2/g'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes-bac|2|abcdabc|3|abcdabc|4|abcdabc ...
Kleiner Schönheitsfehler: das zweite "b" wird nicht ersetzt.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
War das die Bedingung ?? 😲 (So klar ist mir das nämlich noch gar nicht !) Wenn ja, dann macht sowas mit sed keinen Spaß mehr. Denn dann muss man ja wirklich in 2 Stufen ent-schachteln, und das erfordert bei sed ziemliche Verrenkungen. Bei awk geht das ganz einfach, weil das ja schon selber die "Worte" isoliert. Es sind nur 2 winzige Änderungen: 😀 track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> awk -F '|' '{ OFS=FS; while(++i < NF) if($i == 1) { sub("b", "-was-", $(i+1)); sub("d", "-anderes-", $(i+1)) } print }'
abcdabc|1|a-was-c-anderes-abc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes-bac|2|abcdabc|3|abcdabc|4|abcdabc ...
track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> awk -F '|' '{ OFS=FS; while(++i < NF) if($i == 1) { gsub("b", "-was-", $(i+1)); gsub("d", "-anderes-", $(i+1)) } print }'
abcdabc|1|a-was-c-anderes-a-was-c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes--was-ac|2|abcdabc|3|abcdabc|4|abcdabc ... LG, track
|
frostschutz
Anmeldungsdatum: 18. November 2010
Beiträge: 7657
|
Wenn du noch ein Zeichen auf Lager hast das in dem String nicht vorkommen kann, dann (Beispiel mit @ Zeichen) $ sed -r ':repeat; s/(\|1\|[^|@]*)([bd])([^|]*\|2\|)/\1@\2@\3/g; t repeat; s/@b@/etwas/g; s/@d@/anderes/g;'
abcdabc|1|aetwascanderesaetwasc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|etwasacanderesetwasac|2|abcdabc|3|abcdabc|4|abcdabc ... Das funktioniert jedoch nur weil [^|@]* Vorrang hat, also das letzte [bd] zuerst erwischt wird.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Sprachen wir von "ziemlichen Verrenkungen" ? . . . 😀 Aber Deine Idee, die gesuchten Muster zuerst in einer Schleife zu markieren und dann in einem Rutsch zu ersetzen, die ist gut ! Praktisch würde ich es noch etwas ändern. Man kann als Markierung einfach einen Zeilenumbruch \n benutzen. Der kommt sonst garantiert nicht vor. Und es reicht auch 1 Markierung vorher, dann ist es immer noch eindeutig. Damit wird es etwas übersichtlicher: (aber ein sed- Nerd sollte man immer noch sein ... ) track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> sed ':vorne s/\(|1|[^|\n]*\)\(b\|d[^|]*|2|\)/\1\n\2/g; t vorne;'
abcdabc|1|a
bc
da
bc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|
bac
d
bac|2|abcdabc|3|abcdabc|4|abcdabc ...
track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> sed ':vorne s/\(|1|[^|\n]*\)\(b\|d[^|]*|2|\)/\1\n\2/g; t vorne; s/\nb/-was-/g; s/\nd/-anderes-/g;'
abcdabc|1|a-was-c-anderes-a-was-c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes--was-ac|2|abcdabc|3|abcdabc|4|abcdabc ... Übrigens habe ich das "b" oder "d" auch explizit so geschrieben (nicht als Klasse), denn die Muster können ja auch länger als 1 Zeichen sein. LG, track
|
frostschutz
Anmeldungsdatum: 18. November 2010
Beiträge: 7657
|
Dein Code funktioniert bei mir zwar grad nicht, aber \n statt @ ist eine Idee 😬 und (b|d) natürlich auch, aber ich fürchte das Beispiel war so oder so etwas zu stark abstrahiert. 😉
(aber ein sed- Nerd sollte man immer noch sein ...
Ich habe kürzlich ein kompliziertes sed-Konstrukt mit 7-8 Sprungmarken gebaut, aber am Ende war es so langsam, daß ich doch auf C ausgewichen bin. Das obige sed Konstrukt ist auch "langsam", wenn man etwas längere Strings nimmt dann merkt man das. Es fängt eben nach jedem Treffer nochmal von vorne an, um einen weiteren Treffer zu finden.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Oh, sorry, ich hatte tatsächlich das innere Klammernpaar vergessen: (b|d) Das muss man natürlich noch nachtragen, sonst arbeitet die Schleife nicht korrekt: track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> sed ':vorne s/\(|1|[^|\n]*\)\(\(b\|d\)[^|]*|2|\)/\1\n\2/g;'
abcdabc|1|a
bc
dabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|
bac
dbac|2|abcdabc|3|abcdabc|4|abcdabc ...
track@lucid:~$ echo 'abcdabc|1|abcdabc|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|bacdbac|2|abcdabc|3|abcdabc|4|abcdabc ...' |
> sed ':vorne s/\(|1|[^|\n]*\)\(\(b\|d\)[^|]*|2|\)/\1\n\2/g; s/\nb/-was-/g; s/\nd/-anderes-/g;'
abcdabc|1|a-was-c-anderes-a-was-c|2|abcdabc|3|abcdabc|4|abcdab-cabcdabc|1|-was-ac-anderes--was-ac|2|abcdabc|3|abcdabc|4|abcdabc ... Danke für den Hinweis ! LG, track
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Meine Lösung von oben macht es von Anfang an richtig. (:Pfeif:) 💡
|