sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
このsed
スクリプトtab
では、すべての行の前にが挿入されることを期待していますが、$filename
そうではありません。何らかの理由でt
代わりにを挿入しています。
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
このsed
スクリプトtab
では、すべての行の前にが挿入されることを期待していますが、$filename
そうではありません。何らかの理由でt
代わりにを挿入しています。
回答:
のすべてのバージョンがsed
理解できるわけではありません\t
。代わりにリテラルタブを挿入するだけです(Ctrl- Vを押してからTab)。
\t
式の置換部分で理解できるわけではありません(\t
パターンマッチング部分でうまく認識されます)
Bashを使用すると、次のようにプログラムでTAB文字を挿入できます。
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
たが、説明が足りませんでした。実際、私は、おそらく私たちのほとんどがbashで行っているように、あなたがおそらく不完全な理解をしている非常に扱いにくい使用法のためと思います。以下の私の説明を参照してください:stackoverflow.com/a/43190120/117471
$TAB
単一引用符内のように変数を展開しないので、二重引用符を使用する必要があることに注意してください。
*
二重引用符の内側の使用に注意してください...これは意図した正規表現としてではなく、グロブとして扱われます。
@seditは正しいパスにありましたが、変数を定義するのは少し厄介です。
これを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'形式の単語は特別に扱われます。単語は文字列に展開されますに、バックスラッシュでエスケープされた文字はANSI C規格の指定に従って置き換えられます。バックスラッシュエスケープシーケンスが存在する場合は、デコードされます...
展開された結果は、ドル記号が存在しないかのように、単一引用符で囲まれています。
私は個人的に、bashismを回避してもコードが移植可能にならないため、bashを回避するためのほとんどの努力はばかげていると思います。(あなたがコードをシバンしたbash -eu
場合、bashを避けて使用しようとしたsh
場合(あなたが絶対的なPOSIX忍者でない限り)よりもコードの脆弱性は少なくなります。)しかし、それについて宗教的な議論をするのではなく、私はあなたに最高のものを与えます*答え。
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
*ベストアンサー?はい、ほとんどのアンチバッシュシェルスクリプターがコードで間違ったことの1つの例はecho '\t'
、@ robrecordの回答のように使用するためです。GNUエコーでは機能しますが、BSDエコーでは機能しません。これは、http: //pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16のThe Open Groupによって説明されています。これが、バシズムを回避しようとする試みが通常失敗する理由の例です。
を使用し$(echo '\t')
ます。パターンは引用符で囲む必要があります。
例えば。タブを削除するには:
sed "s/$(echo '\t')//"
echo '\t'
は2つの別々の文字を出力します。POSIXポータブルな方法はを使用することprintf '\t'
です。これが私が言う理由です:bashを使用しないことによってコードを移植可能にしようとしないでください。思ったより難しいです。を使用することbash
は、ほとんどの人が実行できる最もポータブルなことです。
sed
実際には、行の前にタブを挿入するだけの場合は、を使用して置換を行う必要はありません。この場合の置換は、特に大きなファイルで作業している場合は、印刷するだけの場合と比べてコストがかかります。正規表現ではないので、読みやすくなっています。
たとえばawkを使用する
awk '{print "\t"$0}' $filename > temp && mv temp $filename
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 __name__ == '__main__':
main(sys.argv[1:])
私は他の人が他のアプローチ(のために適切にこれを明らかにしたと思うsed
、AWK
など)。ただし、私のbash
固有の回答(macOS High SierraおよびCentOS 6/7でテスト済み)は以下のとおりです。
1)OPが最初に提案したものと同様の検索と置換の方法を使用したい場合perl
は、次のように使用することをお勧めします。注:正規表現の括弧の前のバックスラッシュは必要ありません。このコード行は、置換演算子(Perl 5のドキュメントなど)$1
を使用するよりも使用する方が良い方法を反映しています。\1
perl
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2)ただし、ghostdog74で指摘されているように、目的の操作は実際にはtmpファイルを入力/ターゲットファイル()に変更する前に各行の先頭にタブを追加するだけなので、もう一度$filename
お勧めperl
しますが、次の変更を加えます(秒):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3)もちろん、tmpファイルは不必要なので、すべてを「インプレース」で実行し(-i
フラグを追加)、よりエレガントなワンライナーに単純化することをお勧めします。
perl -i -pe $'s/^/\t/' $filename