web-dev-qa-db-ja.com

sedが\ tをタブとして認識しないのはなぜですか?

sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename

このsedスクリプトは、$filenameのすべての行のフォントにタブを挿入すると予想していますが、そうではありません。何らかの理由で、代わりにtを挿入しています。

89

sedのすべてのバージョンが\t。代わりにリテラルタブを挿入するだけです(押します Ctrl-V それから Tab)。

109
Mark Byers

Bashを使用すると、次のようにプログラムでTAB文字を挿入できます。

TAB=$'\t' 
echo 'line' | sed "s/.*/${TAB}&/g" 
echo 'line' | sed 's/.*/'"${TAB}"'&/g'   # use of Bash string concatenation
39
sedit

@seditは正しい道にありましたが、変数を定義するのは少し厄介です。

ソリューション(bash固有)

これをbashで行う方法は、単一引用符で囲まれた文字列の前にドル記号を置くことです。

$ 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

文字列に変数展開を含める必要がある場合は、引用符で囲まれた文字列を次のようにまとめることができます。

$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958  1
1491237958  2
1491237958  3

説明

Bash $'string'は「ANSI-C拡張」を引き起こします。そして、それは私たちのほとんどが\t\r\nなど。 https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting

$ 'string'の形式の単語は特別に扱われます。 Wordは、stringに展開され、バックスラッシュでエスケープされた文字がANSI C標準で指定されているように置き換えられます。バックスラッシュエスケープシーケンスが存在する場合、デコードされます...

展開された結果は、ドル記号が存在しないかのように、単一引用符で囲まれます。

解決策(bashを避ける必要がある場合)

個人的には、bashismを回避してもコードが移植可能にならないため、bashを回避するためのほとんどの努力はばかげていると思います。 (コードをbash -eu bashを避けてsh [あなたが絶対的なPOSIX忍者でない限り]を使用しようとする場合よりも。)しかし、それについて宗教的な議論をするのではなく、私はあなたにBEST *の答えを与えます。

$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
    1
    2
    3

*ベストアンサー?はい、ほとんどのアンチバッシュシェルスクリプターがコードで間違ったことの1つの例はecho '\t'@ robrecord's answer のように。これはGNU echoでは機能しますが、BSD echoでは機能しません。これは http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echoのOpen Groupによって説明されています。 .html#tag_20_37_16 そしてこれは、バシズムを避けようとすることが通常失敗する理由の例です。

22
Bruno Bronosky

Ubuntu 12.04(LTS)のBashシェルで次のようなものを使用しました。

最初が一致したときにtab、secondで新しい行を追加するには:

sed -i '/first/a \\t second' filename

firsttab、secondに置き換えるには:

sed -i 's/first/\\t second/g' filename
8
Thomas Bratt

$(echo '\t')を使用します。パターンを引用符で囲む必要があります。

例えば。タブを削除するには:

sed "s/$(echo '\t')//"
3
robrecord

sedを使用して置換を行う必要はありませんが、実際には、行の前にタブを挿入するだけです。この場合の置換は、特に大きなファイルで作業している場合は特に、印刷する場合と比べて費用のかかる操作です。正規表現ではないので読みやすくなっています。

例:awkを使用する

awk '{print "\t"$0}' $filename > temp && mv temp $filename
3
ghostdog74

私はMacでこれを使用しました:

sed -i '' $'$i\\\n\\\thello\n' filename

参照用にこのリンクを使用

1
Raj Hassani

BSD sedの代わりに、Perlを使用します。

ct@MBA45:~$ python -c "print('\t\t\thi')" |Perl -0777pe "s/\t/ /g"
   hi
0
Cees Timmerman

sed\tをサポートしていません。また、\nなどの他のエスケープシーケンスもサポートしていません。私が見つけた唯一の方法は、sedを使用してスクリプトにタブ文字を実際に挿入することでした。

そうは言っても、PerlまたはPythonの使用を検討することもできます。ここに短いPythonすべてのストリーム正規表現に使用することを書いたスクリプトがあります:

#!/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 __== '__main__':
  main(sys.argv[1:])
0
Roman Nurik

他のアプローチ(sedAWKなど)に対して、他の人がこれを適切に明確にしたと思います。ただし、私のbash固有の回答(macOS High SierraおよびCentOS 6/7でテスト済み)は次のとおりです。

1)OPが最初に提案したものと同様の検索と置換の方法を使用したい場合、次のようにPerlを使用することをお勧めします。 注:正規表現の括弧の前のバックスラッシュは不要であり、このコード行は、Perl置換演算子で_$1_よりも_\1_を使用する方が良い方法を反映しています(例: Perl 5ドキュメント )。

_Perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
_

2)ただし、 ghostdog74 で指摘されているように、望ましい操作は実際には単純にタブを追加であるため、tmpファイルをinput /ターゲットファイル(_$filename_)、もう一度Perlをお勧めしますが、次の変更が必要です:

_Perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
Perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
_

3)もちろん、tmpファイルはsuperfluousであるため、すべてを「インプレース」(_-i_フラグを追加)して、よりエレガントなワンライナーに単純化することをお勧めします

_Perl -i -pe $'s/^/\t/' $filename
_
0
justincbagley