web-dev-qa-db-ja.com

Solarisの「sed -i」コマンドに代わるものはありますか?

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

ただし、これも機能しません。

私はPerlawkにも代替案を見つけましたが、Solaris sedの解決策は大いに評価されます。

私はこのバージョンのbashを使用しています:

GNU bash、バージョン3.2.57(1)-release(sparc-Sun-solaris2.10)

11
tpsaitwal

ed を使用します。ほとんどのプラットフォームで利用でき、ファイルをその場で編集できます。
sededに基づいているため、パターンを置換するための構文は同様です。

ed -s infile <<\IN
,s/old/new/g
w
q
IN
15
don_crissti

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または同様のユーティリティを使用して、一時ファイルの一意の名前を生成できます。

8
John1024

まず、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も。


exsedの大きな違いの1つは、sedstreamエディターであることです。行ごとに順次動作するだけです。 exはそれよりもはるかに柔軟であり、実際にexで直接インタラクティブなテキストファイル編集を行うことができます。 viの前身です。

3
Wildcard

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ファイルを作成するのではなく、最初に注意してコピーすることに注意してください。これは、ファイルが複数の名前で知られている(つまり、ハードリンクがある)場合に重要です。リンクを壊さないでください。

3
Toby Speight

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. 内容を1つだけ「通過」する-シェルがファイル記述子を「シーク」する方法はないportableがないため、プログラムが一部の内容を読み取ると、他のプログラムは表示のみを行うファイルの残りの部分。そしてsedはファイル全体を読み込みます。

  2. Shell/script/sedが終了する前にkillされた場合、元のファイルが失われる可能性が少しあります。

1
mtraceur