Wie kann ich in linux sed mehrere Instanzen eines Zeichens durch die gleiche Anzahl von Instanzen eines anderen Zeichens ersetzen?

398
xman

Ich muss einen sich wiederholenden Satz von Zeichen (2 oder mehr) durch die genaue Anzahl der Ersatzzeichen ersetzen. Ich muss dies entweder mit sed oder in vi tun.

Beispiele

"abc,,,def" becomes "abc|||dev" "1245d,,,,,22" becomes "1245d|||||22" 

Vielen Dank

2
Sie benötigen also einen globalen Ersatz eines Zeichens durch ein anderes in einer Datei? Alex vor 7 Jahren 0
Ja, aber nur, wenn es mehr als zwei Wiederholungen gibt. xman vor 7 Jahren 0
Dies kann leicht mit `perl` erreicht werden:` perl -lapes /, / "|" x Länge ($ &) / ge'`. Schade, dass Sie es nicht benutzen können (warum?). Es kann * von innerhalb von "vi" aus aufgerufen werden, weiß nicht, ob dies in Ihrer Situation zulässig ist. simlev vor 7 Jahren 0

3 Antworten auf die Frage

1
jvb

Pipe durch sed, wie

echo "abc,,,def" | sed 's/,/|/g'

aber ich würde empfehlen zu verwenden

tr ',' '|'

in diesem Fall.

Das würde nicht funktionieren. Wie in der Frage erwähnt, muss es mit 2 oder mehr Wiederholungen des Charakters übereinstimmen. xman vor 7 Jahren 0
Danke für die Klarstellung, meine Schuld. Sie möchten also nur die Teile in Klammern ersetzen: echo "a, bc ,,, def" | sed -E 's / (,, +) / (\ 1) / g' -> a, bc (,,,) def - jetzt forschen ... jvb vor 7 Jahren 0
teilweise "Lösung": `Echo 'a, b ,, c ,,, c" | sed 's / ,,,, / |||| / g; s / ,,, / ||| / g; s / ,, / || / g '`(erweitern Sie ggf. das Muster). Nicht wirklich eine schöne Lösung, aber * könnte * nützlich sein, wenn die maximale Anzahl von Wiederholungen bekannt ist. jvb vor 7 Jahren 0
Richtig ... Ihr Ansatz würde funktionieren, wenn wir die maximale Anzahl von Wiederholungen kennen. Wäre gut, wenn es eine generische Lösung gibt. xman vor 7 Jahren 0
Was ist mit "echo" a, b ,, c ,,, c "| sed -E's / ([^,]), ([^,]) / \ 1 # \ 2 / g; s /, / | / g; s / # /, / g'`? Dies konvertiert alle * einzelnen * "," in "#", dann alle verbleibenden (= mehrere) "," in "|" und dann alle gespeicherten "#" in ",". Es benötigt jedoch ein Zeichen, das im Eingabestrom nicht verwendet wird (hier #). jvb vor 7 Jahren 0
das würde funktionieren. Sie haben Recht, es braucht einen ungenutzten Charakter ... der zum Glück verfügbar ist. xman vor 7 Jahren 0
1
Paulo

Entschuldigen Sie, dass ich nicht kommentiert habe. Ich habe keinen guten Ruf.

Diese Lösung schlägt fehl, wenn mehr Muster vorhanden sind, wie z abc,,,def,g.

sed -n 's/[^,],,/&/;tsubs;p;d;:subs s/,/|/g;p' <<<'abc,,,def abc,,def abc,,,def,g abc,def' 
0
Michael Vehrs

Dies ist praktisch unmöglich, da reguläre Ausdrücke funktionieren. Wie bereits in jvb dargelegt, ist eine Lösung einfach (wenn auch nicht notwendigerweise kurz), wenn die maximale Anzahl aufeinanderfolgender Quellzeichen bekannt ist. Wenn nicht, können Sie zuerst alle Quellzeichen ändern und dann die einzelnen Zeichen in einem zweiten Schritt zurücksetzen. Dies funktioniert jedoch nur, wenn das Zielzeichen nicht im Eingabestrom vorkommt oder wenn Sie ein Zeichen verwenden können, von dem bekannt ist, dass es nicht im Eingabestrom als Zwischenziel vorkommt.

Außerdem müssen Sie die Eckfälle eines einzelnen Quellzeichens berücksichtigen, die am Anfang oder am Ende der Zeile vorkommen. Somit:

tr ',' '|' < file | sed 's/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/' 

oder

sed 's/,/|/g;s/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/' file 

Eine Lösung, die eine Sprache verwendet, die die Länge der Zeichenkette kennt, wäre robuster.