2017-02-20 6 views
3

Ich habe eine mehrzeilige Variable, die ich von STDOUT erfasst.
Ich möchte einen Echo-Befehl mit dieser mehrzeiligen Variable in Zeile 15 in einem anderen Skript (Ziel) einfügen.Verwendung von Multiline-Variable in sed-Befehl

#!/bin/bash 
TEST=`cat foo` 

echo "$TEST" 

sed -i "15i echo \"$TEST\" > someotherfile" target 

Inhalt von foo:

apples 
oranges 
bananas 
carrots 

dachte ich, die sed-Befehl in Zeile Feeds lesen, die ich meine foo hat bestätigt:

[email protected]$ cat foo | tr -cd '\n' | wc -c 
4 

Wenn ich meine test.sh Skript ausführen Ich sehe, was in $TEST ist, aber bekomme einen Fehler für den sed Befehl:

[email protected]$ ./test.sh 
apples 
oranges 
bananas 
carrots 
sed: -e expression #1, char 18: unknown command: `o' 

Was mache ich falsch? Vielen Dank im Voraus.

+0

können Sie vollständige Eingabe/Ausgabe hinzufügen? 'TEST' hat mehrere Zeilen, und Sie fügen eine Zeichenfolge davor und danach hinzu ... daher ist es für mich etwas unklar, ... einfach Inhalte aus einer Datei einzufügen, verwenden Sie den' r' Befehl ... 'sed ' 14r foo 'target' -> fügt den Inhalt der Datei foo ab der 15. Zeile in das Ziel der Eingabedatei ein – Sundeep

Antwort

1

Versuchen:

Solution1:

awk 'NR==15{print;system("cat foo");next} 1' Input_file 

Keine Notwendigkeit, die komplette Datei in eine Variable zu bekommen, könnten wir einfach ausdrucken je nachdem, welche Zeile von input_file Sie es drucken möchten.

Solution2:

line=15; sed -e "${line}r foo" target 

oder (im Skript-Modus)

cat script.ksh 
line=15; 
sed -e "${line}r foo" target 

Wo Sie die Anzahl der Zeile ändern könnten, wo Sie die Zeilen aus einer anderen Datei eingefügt werden sollen.

0

Der i-Befehl in sed fügt die Textzeilen ein, die mit einem Zeilenumbruch enden, bis zu einer Zeile, die nicht mit einem umgekehrten Schrägstrich endet. Die Befehle a und c sind ähnlich. Klassisch sed mag nicht die erste Zeile in der gleichen Zeile wie der i Befehl angezeigt werden; GNU sed ist nicht so wählerisch.

Wenn Sie den Befehl manuell geschrieben haben, würden Sie schreiben müssen:

15i\ 
echo "apples\ 
oranges\ 
bananas\ 
carrots" > someotherfile 

Es geht jetzt ist „wie wollen Sie dies die Datei foo enthält die Liste der Namen gegeben schaffen?“. Manchmal ist es nützlich, sed zu verwenden, um das Skript sed zu generieren. Es kann jedoch auch kompliziert sein, wenn Sie an den Enden von Linien, die einem i (oder a oder c) -Befehl unterliegen, Backslashes erhalten, und es ist einfacher, das Problem zu umgehen.

{ 
    echo "15i\\" 
    sed -e '1s/^/echo "/' -e 's/$/\\/' -e '$s/\\$/" > someotherfile/' foo 
} | sed -f /dev/stdin target 

GNU sed kann sein Skript aus der Standardeingabe mit -f - lesen; BSD (macOS) sed mag das nicht, aber Sie können stattdessen -f /dev/stdin verwenden (was auch mit GNU sed funktioniert), zumindest auf Systemen, wo es eine /dev/stdin gibt.

0

GNUsed wird angenommen, wie durch die Syntax in der Frage impliziert.

#!/bin/bash 

# Read contents of file 'foo' into shell variable $test. 
test=$(<foo) 

# \-escape the newlines in $test for use in Sed. 
testEscapedForSed=${test//$'\n'/\\$'\n'} 

sed -i "15i echo \"$testEscapedForSed\" > someotherfile" target 
  • Ihr Problem war, dass mehrzeiligen Strings sed Funktionen wie i (insert), die in diesen Strings, die die Zeilenumbrüche eingebettet erfordert \ -escaped zu sein, so dass sed weiß wo die Zeichenfolge endet und zusätzliche Befehle, falls vorhanden, starten.

    • A (Nicht-Standard) parameter expansion wird verwendet, um alle Zeilenumbrüche in $test mit sich selbst durch \ Präfix zu ersetzen, unter Verwendung von ANSI C-quoted string$'\n' tatsächlichen Neue-Zeile-Zeichen zu erzeugen.
  • Beachten Sie auch:

    • I TEST-test umbenannt haben, weil all-uppercase shell-variable names should be avoided.

    • Ich habe moderne Befehlsersetzungssyntax $(..) anstelle der alten Syntax `...` verwendet.

    • $(<foo) ist eine etwas effizientere - wenn auch nicht standardmäßige - Möglichkeit, den Inhalt einer Datei gleichzeitig zu lesen.

0

Interessante Frage. Wie bereits erwähnt, ist die ganze Geschichte für sed, um mehrzeiligen Text in eine andere Datei einfügen zu können, dass dieser neue mehrzeilige Text tatsächlich literal \n für Zeilenumbrüche haben muss.

So können wir sed verwenden echte neue Zeile Zeichen zu wörtlichen \n zu konvertieren:

$ a=$(tr '\n' '\\' <file3 |sed 's#[\]$##' |sed "s#[\]#\0n#g") 
#Alternative: a=$(sed "s#[\]#\0n#g" <(sed 's#[\]$##' <(tr '\n' '\\' <file3))) 
$ echo "$a" 
apples\noranges\nbananas\ncarrots 

Wie diese Übersetzung funktioniert:
* Zuerst haben wir alle neue Linien mit einem einzigen Backslash ersetzen mit tr
* Dann wir entfernen den umgekehrten Schrägstrich vom Ende des Strings
* Dann ersetzen wir alle anderen Backslashes mit Backaslash und n Char.

Da nun variable $a enthält wörtliche \n zwischen den Zeilen, sed wird sie zurück in Actuall neue Linien übersetzen:

$ cat file4 
Line1 
line2 
line3 
$ sed "2i $a" file4 
Line1 
apples 
oranges 
bananas 
carrots 
line2 
line3 

Ergebnis:
Mutliline Ersatz kann mit zwei Befehle erfolgen:

$ a=$(tr '\n' '\\' <file3 |sed 's#[\]$##' |sed "s#[\]#\0n#g") 
$ sed "2i $a" file4 

sed 2i bedeutet einen Text vor Zeile2 einfügen. 2a kann verwendet werden, um etwas nach Zeile2 einzufügen.

Bemerkung:
Nach this post, die ein Duplikat zu sein scheinen, die Übersetzung von neuen Linien zu wörtlichen \ n scheint, dass mit nur geschehen:

a=$(echo ${a} | tr '\n' "\\n") 

Aber diese Methode nie in meinem System gearbeitet.

Remark2:
Der sed Betrieb sed "2i $a" = Variable $ a einsetzen, bevor Zeile 2 kann auch mit den gleichen Zeichen \0 zuzüglich einer neuen Zeile \n sowie den Inhalt der Variablen $a als sed "1 s/.*/\0\n$a/" = ersetzen alle Zeichen der ersten Zeile ausgedrückt werden => füge $ a nach Zeile1 ein = füge $ a vor Zeile2 ein.