
私のプロジェクトでは、次のようなファイル内の既存のテキストを置き換える必要があります。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) リリース (sparc-sun-solaris2.10)
答え1
使用ed
ほとんどのプラットフォームで利用でき、ファイルをその場で編集できます。はパターンを置換するための構文に基づいている
ため、次のようになります。sed
ed
ed -s infile <<\IN
,s/old/new/g
w
q
IN
答え2
GNU sed をインストールできない場合は、以下を使用します。
sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt
これはリダイレクションsed の出力を一時ファイルに送信します。sed が正常に完了すると、abc.txt
一時ファイルに上書きされます。
GNU sedのソースコードからわかるように、これはまさに と同じことですsed -i
。つまり、これは とほぼ同じくらい効率的ですsed -i
。
がすでに存在する可能性がある場合は、または同様のユーティリティを使用して、一時的な一意の名前を生成すること abc.tmp
をお勧めします。mktemp
答え3
と同等のものが欲しい場合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
。これは、ファイルが複数の名前で認識されている場合 (つまり、ハード リンクがある場合) で、リンクが壊れないようにしたい場合に重要です。
答え4
使用sed
となし見える一時ファイル:
別途作成する必要がなくなります見える「一時ファイル」:
exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-
説明
Unix系システムでは、ファイルの内容をディスクから削除するのは、両方ファイルシステム内でリンクされていない、そしてどのプロセスでも開かれません。したがって、exec 3<
ファイル記述子 3 の読み取り用にシェルでファイルを開き、rm
ファイル (ファイル システムからリンク解除されます) を呼び出し、sed
ファイル記述子 3 を入力として使用して呼び出すことができます。
これはとてもこれとは異なります:
# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt
違いは、1 つのコマンドで実行する場合、シェルは同じファイルを読み取りと書き込みの両方で開き、ファイルを切り捨てるオプションが付くことです。同じファイルなので、内容は失われます。ただし、読み取り用に開き、次にrm
同じパス名で書き込み用に開くと、実際には同じパス名で新しいファイルが作成されます (ただし、元のファイルは開いたままなので、新しい inode とディスクの場所になります)。そのため、内容は引き続き使用できます。
完了したら、以前に開いたファイル記述子を閉じることができます (これがexec 3<&-
特別な構文の機能です)。これにより、元のファイルが解放され、オペレーティング システムがディスク領域を削除 (未使用としてマーク) できるようになります。
注意点
このソリューションについては、いくつか留意すべき点があります。
コンテンツは一度しか見ることができず、ポータブルシェルがファイル記述子を「シーク」する方法 - つまり、プログラムが内容の一部を読み取ると、他のプログラムはファイルの残りの部分のみを参照します。そして、
sed
ファイル全体を読み取ります。シェル/スクリプト/sed が完了する前に強制終了された場合、元のファイルが失われる可能性がわずかにあります。