foo
のようなファイル内の既存のテキストをfooofoo
のような他のテキストに置き換えるというプロジェクトの要件があります。
abc.txt
name
foo
foo1
だから私は試しました:
sed -i "s/foo/fooofoo/g" abc.txt
しかし、私はこのエラーを受け取ります:
sed:不正なオプション-
i
私は使用しなければならないことをマニュアルで見つけました:
sed -i\ "s/foo/fooofoo/g" abc.txt
ただし、これも機能しません。
私はPerl
とawk
にも代替案を見つけましたが、Solaris sed
の解決策は大いに評価されます。
私はこのバージョンのbashを使用しています:
GNU bash、バージョン3.2.57(1)-release(sparc-Sun-solaris2.10)
ed
を使用します。ほとんどのプラットフォームで利用でき、ファイルをその場で編集できます。sed
はed
に基づいているため、パターンを置換するための構文は同様です。
ed -s infile <<\IN
,s/old/new/g
w
q
IN
GNU sedをインストールできない場合は、以下を使用します。
sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt
これは、redirectionを使用して、sedの出力を一時ファイルに送信します。 sedが正常に完了すると、abc.txt
が一時ファイルで上書きされます。
GNU sed のソースコードからわかるように、これはsed -i
とまったく同じです。つまり、これはsed -i
とほぼ同じくらい効率的です。
abc.tmp
がすでに存在している可能性がある場合は、mktemp
または同様のユーティリティを使用して、一時ファイルの一意の名前を生成できます。
まず、i\
参照しているコマンドは、1行のテキストを挿入するためのものです—編集したテキストをファイルに保存するためのものではありません。そのためにsed
を使用する POSIXで指定された方法 はありません。
あなたができることは ex
を使用することです。これはisPOSIXで指定 :
printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt
x
コマンドは「保存して終了」するためのものです。 wq
も使用できますが、x
の方が短いです。
%
置換コマンドの先頭にある記号は、「このコマンドをバッファ内のすべての行に適用する」ことを意味します。 1,$s/find/replace/g
も。
ex
とsed
の大きな違いの1つは、sed
がstreamエディターであることです。行ごとに順次動作するだけです。 ex
はそれよりもはるかに柔軟であり、実際にex
で直接インタラクティブなテキストファイル編集を行うことができます。 vi
の前身です。
sed -i.bak
に相当するものが必要な場合は、非常に簡単です。
GNU sed:
#!/bin/sh
# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"
# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'
##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########
# Prove that it worked
ls "$dir"
cat "$dir/foo"
マークされた行を単純に置き換えることができます
cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"
これにより、既存のファイルがバックアップとして移動され、新しいファイルが書き込まれます。
同等のものが必要な場合
sed -i -e "$script" "$dir" # no backup
その後、少し複雑になります。ファイルを開いて標準入力として読み込み、リンクを解除してから、sedの出力を置き換えて置き換えます。
( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )
これをサブシェルで行うため、元のstdinはこの後も引き続き使用できます。入力を切り替えてサブシェルなしで元に戻すことは可能ですが、この方法は私にはより明確に思えます。
新しいfoo
ファイルを作成するのではなく、最初に注意してコピーすることに注意してください。これは、ファイルが複数の名前で知られている(つまり、ハードリンクがある)場合に重要です。リンクを壊さないでください。
sed
を使用し、visible一時ファイルを使用しない場合:別のvisible "temp file"の作成を回避できます。
exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-
両方ファイルシステムでリンク解除されるまで、Unixライクなシステムは実際にはディスクからファイルの内容を削除しませんおよびどのプロセスでも開かれません。だからあなたはexec 3<
ファイル記述子3を読み取るためにシェルでファイルを開くには、rm
ファイル(ファイルシステムからリンクを解除します)を使用し、ファイル記述子3を使用してsed
を呼び出します。入力。
これはveryとは異なることに注意してください。
# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt
違いは、1つのコマンドでそれを実行すると、シェルは同じファイルを読み取り用とファイルの切り捨てオプションを使用した書き込み用の両方に開くだけです-まだ同じファイルであるため、内容が失われます。しかし、それを読み取り用に開いてからrm
し、次に同じパス名を開いて書き込みを行うと、実際には同じパス名で新しいファイルが作成されます(ただし、元のiノードとディスクの場所にはまだ開いています):したがって、コンテンツはまだ利用可能です。
その後、完了したら、以前に開いたファイル記述子を閉じることができます(これがexec 3<&-
特別な構文は、オペレーティングシステムがそのディスクスペースを削除(未使用としてマーク)できるように、元のファイルを解放します。
このソリューションについては、いくつかの注意点があります。
内容を1つだけ「通過」する-シェルがファイル記述子を「シーク」する方法はないportableがないため、プログラムが一部の内容を読み取ると、他のプログラムは表示のみを行うファイルの残りの部分。そしてsed
はファイル全体を読み込みます。
Shell/script/sedが終了する前にkillされた場合、元のファイルが失われる可能性が少しあります。