sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Ich erwarte, dass dieses sed
Skript tab
vor jeder Zeile ein $filename
einfügt, dies ist jedoch nicht der Fall. Aus irgendeinem Grund wird t
stattdessen ein eingefügt.
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
Ich erwarte, dass dieses sed
Skript tab
vor jeder Zeile ein $filename
einfügt, dies ist jedoch nicht der Fall. Aus irgendeinem Grund wird t
stattdessen ein eingefügt.
Antworten:
Nicht alle Versionen von sed
verstehen \t
. Fügen Sie stattdessen einfach eine wörtliche Registerkarte ein (drücken Sie Ctrl- Vdann Tab).
\t
im Ersatzteil des Ausdrucks (es wird \t
im Mustervergleichsteil gut erkannt )
Mit Bash können Sie ein TAB-Zeichen programmgesteuert wie folgt einfügen:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
aber fehlenden Erklärung. Tatsächlich vermute ich aufgrund der äußerst umständlichen Verwendung, dass Sie wahrscheinlich ein unvollständiges Verständnis haben (wie die meisten von uns mit Bash). Siehe meine Erklärung unten: stackoverflow.com/a/43190120/117471
$TAB
in einfachen Anführungszeichen erweitert, daher müssen Sie doppelte Anführungszeichen verwenden.
*
doppelten Anführungszeichen ... dies wird als Glob behandelt, nicht als Regex, den Sie beabsichtigen.
@sedit war auf dem richtigen Weg, aber es ist etwas umständlich, eine Variable zu definieren.
Die Möglichkeit, dies in Bash zu tun, besteht darin, ein Dollarzeichen vor Ihre einfache Anführungszeichenfolge zu setzen.
$ echo -e '1\n2\n3'
1
2
3
$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
1
2
3
Wenn Ihre Zeichenfolge eine Variablenerweiterung enthalten muss, können Sie Zeichenfolgen in Anführungszeichen wie folgt zusammenstellen:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
In Bash $'string'
verursacht "ANSI-C-Erweiterung". Und das ist es, was die meisten von uns erwarten , wenn wir Dinge verwenden , wie \t
, \r
, \n
usw. Von: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
Wörter der Form $ 'string' werden speziell behandelt. Das Wort wird zu Zeichenfolge erweitert , wobei Zeichen mit Backslash-Escapezeichen gemäß ANSI C-Standard ersetzt werden. Backslash-Escape-Sequenzen werden, falls vorhanden, dekodiert ...
Das erweiterte Ergebnis wird in einfachen Anführungszeichen gesetzt, als ob das Dollarzeichen nicht vorhanden gewesen wäre.
Ich persönlich halte die meisten Bemühungen, Bash zu vermeiden, für dumm, weil das Vermeiden von Bashismen Ihren Code NICHT portabel macht. (Ihr Code wird weniger spröde sein, wenn Sie ihn verprügeln, bash -eu
als wenn Sie versuchen, Bash zu vermeiden und zu verwenden sh
[es sei denn, Sie sind ein absoluter POSIX-Ninja].) Aber anstatt ein religiöses Argument darüber zu haben, gebe ich Ihnen nur das BESTE * Antworten.
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* Beste Antwort? Ja, denn ein Beispiel dafür, was die meisten Anti-Bash-Shell-Scripter in ihrem Code falsch machen würden, ist die Verwendung echo '\t'
wie in der Antwort von @ robrecord . Das funktioniert für GNU-Echo, aber nicht für BSD-Echo. Dies wird von The Open Group unter http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16 erklärt . Dies ist ein Beispiel dafür, warum der Versuch, Bashismen zu vermeiden, normalerweise fehlschlägt.
Ich habe so etwas mit einer Bash-Shell unter Ubuntu 12.04 (LTS) verwendet:
So fügen Sie eine neue Zeile mit der Registerkarte hinzu: die zweite, wenn die erste übereinstimmt:
sed -i '/first/a \\t second' filename
So ersetzen Sie zuerst mit Registerkarte zweiten :
sed -i 's/first/\\t second/g' filename
\\t
und nicht \t
.
Verwenden $(echo '\t')
. Sie benötigen Anführungszeichen um das Muster.
Z.B. So entfernen Sie eine Registerkarte:
sed "s/$(echo '\t')//"
echo '\t'
2 separate Zeichen aus. Die tragbare Methode von POSIX ist die Verwendung printf '\t'
. Deshalb sage ich: Versuchen Sie nicht, Ihren Code portabel zu machen, indem Sie keine Bash verwenden. Es ist schwieriger als du denkst. Verwenden bash
ist das tragbarste, was die meisten von uns tun können.
Sie müssen keine sed
Ersetzung durchführen, wenn Sie tatsächlich nur eine Registerkarte vor der Zeile einfügen möchten. Das Ersetzen dieses Falls ist im Vergleich zum Ausdrucken ein teurer Vorgang, insbesondere wenn Sie mit großen Dateien arbeiten. Es ist auch einfacher zu lesen, da es kein Regex ist.
zB mit awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
Ich habe dies auf dem Mac verwendet:
sed -i '' $'$i\\\n\\\thello\n' filename
sed
unterstützt nicht \t
, noch andere Escape-Sequenzen wie \n
für diese Angelegenheit. Die einzige Möglichkeit, die ich gefunden habe, bestand darin, das Tabulatorzeichen mithilfe von in das Skript einzufügensed
.
Möglicherweise möchten Sie jedoch Perl oder Python verwenden. Hier ist ein kurzes Python-Skript, das ich geschrieben habe und das ich für alle Stream-Regexing verwende:
#!/usr/bin/env python
import sys
import re
def main(args):
if len(args) < 2:
print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
raise SystemExit
p = re.compile(args[0], re.MULTILINE | re.DOTALL)
s = sys.stdin.read()
print p.sub(args[1], s),
if __name__ == '__main__':
main(sys.argv[1:])
Anstelle von BSD sed verwende ich Perl:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
Ich denke, andere haben dies für andere Ansätze angemessen geklärt ( sed
,AWK
usw.). Es bash
folgen jedoch meine spezifischen Antworten (getestet unter macOS High Sierra und CentOS 6/7).
1) Wenn OP eine Such- und Ersetzungsmethode verwenden möchte, die der ursprünglich vorgeschlagenen ähnelt, würde ich vorschlagen, perl
diese wie folgt zu verwenden. Hinweise: Backslashes vor Klammern für Regex sollten nicht erforderlich sein. Diese Codezeile zeigt, wie sie $1
besser verwendet werden kann als \1
mit dem perl
Substitutionsoperator (z. B. gemäß Perl 5-Dokumentation ).
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) Wie jedoch von ghostdog74 hervorgehoben , würde ich , da die gewünschte Operation darin besteht, einfach eine Registerkarte am Anfang jeder Zeile hinzuzufügen , bevor die tmp-Datei in die Eingabe- / Zieldatei ( $filename
) geändert wird , perl
erneut empfehlen , jedoch mit der folgenden Änderung (s):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) Natürlich ist die tmp-Datei überflüssig , daher ist es besser, einfach alles an Ort und Stelle zu tun ( -i
Flag hinzufügen ) und die Dinge zu einem eleganteren Einzeiler zu vereinfachen
perl -i -pe $'s/^/\t/' $filename