2017-04-12 5 views
0

ich folgendes Bash-Skript haben:Schleife durch eine Datei und sed Ersatz jede Zeile

while IFS= read -r line; do 
     line=$(echo $line | sed "s/\'/\'\'/") 

     [[ $line =~ ^\<ID\>(.*) ]] && printf "${BASH_REMATCH[1]}" 
done < <(dos2unix < file) 

editierte Version des Skripts ohne dos2unix:

while IFS= read -r line && line=${line%$'\r'}; do 
    [[ $line =~ ^\<ID\>(.*) ]] && printf "${BASH_REMATCH[1]}" 
done < file 

ich jeden Apostroph in "file" ersetzen wollen mit 2 Apostrophen vor Ich Schleife durch es. Wie kann ich das machen? Ich wäre dankbar für Vorschläge bezüglich einer der beiden Versionen. WICHTIG Im NICHT dürfen die Originaldatei ändern !!

+0

Sie haben '' drin. Ist das XML? Wenn ja, dann wäre es eine sehr gute Idee, einen XML-Parser zu verwenden. – Sobrique

+0

@Sobrique Ich extrahiere Daten aus Textdateien, um eine SQL-Datei zu erstellen und dann lese ich .read.sql, um die Datenbank zu erstellen und zu laden –

+0

Was genau "funktioniert nicht richtig" bedeutet? –

Antwort

0

Dies ist ein Job für sed allein:

sed 's/\r$//;s/\'/\'\'/g;s/^<ID>\(.*\)/\1/p;d' < file 

Die Schritte sind:

  1. sed als mehrere mehrere Befehle getrennt mit Zeilenumbrüchen, Semikolons oder gegeben akzeptiert -e Optionen.
  2. sed 's/\r$//; entfernt die CR am Ende jeder Zeile wie dos2unix.
  3. Die g Flagge hinzugefügt zu s/\'/\'\'/ bedeutet ersetzen alle Vorkommen in der Linie; Standard ist es, nur einen zu ersetzen.
  4. Die s/^<ID>\(.*\)/\1/ tut das Äquivalent dieser bash Regex und dem p Flagge am Ende nun, sind die passenden Linien Sed Druck macht, weil
  5. d Der Befehl, um die Zeile entfernt, so dass es nicht standardmäßig gedruckten erhalten (Sie könnten das mit der Option -n stattdessen tun).

Auf einer Seite zur Kenntnis, meine zsh akzeptiert keine \' in ', also würde ich wahrscheinlich schreiben Sie es

sed -n -e 's/\r$//' -e "s/'/''/g" -e 's/^<ID>\(.*\)/\1/p' 

Es sollte gleichwertig sein, nur das Zitat Stil, separate Optionen Schalten und die -n anstelle des endgültigen d.

+0

Befehle wie diese veranschaulichen, warum die Macht von Ein Werkzeug wie "sed" ist begrenzt durch die Fähigkeit des menschlichen Gehirns, seine "effiziente" Syntax zu entschlüsseln, sobald die Anweisungen nicht mehr trivial sind. – Fred

+0

Dies ist übrigens keine Kritik von 'sed' oder Ihrer vorgeschlagenen Lösung, nur eine Beobachtung, dass es eindeutig einen Kompromiss in Lesbarkeit und Wartbarkeit gibt, der erforderlich ist, um den" magischen Einzeiler, der alles tut "zu erreichen. – Fred

+0

@Fred, das waren die Tage von 4800-Baud Fernschreibern, bei denen jeder Tastendruck sichtbare Verzögerung verursachte, so dass jeder so wenig wie möglich tippen wollte. –

0

Obwohl dies keine "Lösung" ist (Ihre Frage ist nicht klar, was nicht in Ihrem Code funktioniert), sollten Sie sicher vermeiden, sed für jede einzelne Zeile zu rufen. Es ist nicht "falsch" im Sinne eines falschen Ergebnisses, aber es ist so viel langsamer, dass es vermieden werden sollte. Es gibt Wege, die sowohl schneller als auch einfacher zu programmieren sind.

Do it diese Weise:

while IFS= read -r line; do 
     [[ $line =~ ^\<ID\>(.*) ]] && printf "${BASH_REMATCH[1]}" 
done < <(dos2unix < file | sed "s/\'/\'\'/") 
+0

Das ist genau das, was ich tun möchte, aber wenn ich den sed Teil hinzufüge, macht das Skript nichts –