web-dev-qa-db-ja.com

sed -i(インプレース編集)で移植性を実現するにはどうすればよいですか?

私は自分のサーバー用のシェルスクリプトを書いています。これは、FreeBSDを実行する共有ホスティングです。また、Linuxを実行しているPCでローカルにテストできるようにしたいと考えています。したがって、私はそれらを移植可能な方法で書き込もうとしていますが、sedではそれを行う方法がありません。

私のウェブサイトの一部は生成された静的HTMLファイルを使用しており、このsed行は各再生成後に正しいDOCTYPEを挿入します:

sed -i '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

LinuxではGNU sedと連動しますが、FreeBSD sed-iオプションの後にある最初の引数がバックアップコピーの拡張であると想定しています。これはそれがどのように見えるか:

sed -i '' '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

ただし、GNU sedは、式が-iの直後に続くことを期待します。(また、改行処理の修正が必要ですが、すでに回答されています ここ

もちろん、この変更をスクリプトのサーバーコピーに含めることはできますが、バージョン管理にVCSを使用するのは面倒です。完全に移植可能な方法でsedを使用してこれを達成する方法はありますか?

46
Red

GNU sedは-iの後にオプションの拡張子を受け入れます。拡張子は、間にスペースを入れずに同じ引数にある必要があります。この構文は、BSD sedでも機能します。

sed -i.bak -e '…' SOMEFILE

BSDでは、-iは複数の入力ファイルがある場合の動作も変更することに注意してください。これらは独立して処理されます(たとえば、$は各ファイルの最後の行と一致します)。また、これはBusyBoxでは機能しません。

バックアップファイルを使用したくない場合は、使用可能なsedのバージョンを確認できます。

case $(sed --help 2>&1) in
  *GNU*) set sed -i;;
  *) set sed -i '';;
esac
"$@" -e '…' "$file"

または、位置パラメータを壊さないようにするには、関数を定義します。

case $(sed --help 2>&1) in
  *GNU*) sed_i () { sed -i "$@"; };;
  *) sed_i () { sed -i '' "$@"; };;
esac
sed_i -e '…' "$file"

気になりたくない場合は、Perlを使用してください。

Perl -i -pe '…' "$file"

移植可能なスクリプトを作成する場合は、-iを使用しないでください。POSIXにはありません。 sedが内部で行うことを手動で実行します。これは、あと1行のコードです。

sed -e '…' "$file" >"$file.new"
mv -- "$file.new" "$file"

sedを素敵にプレイさせるトリックが見つからない場合は、次の方法を試してみてください:

  1. -iは使用しないでください。

    sed '1s/^/<!DOCTYPE html> \n/' "${file_name.html}" > "${file_name.html}.tmp" &&
      mv "${file_name.html}.tmp" "${file_name.html}"
    
  2. Perlを使用する

    Perl -i -pe 'print "<!DOCTYPE html> \n" if $.==1;' "${file_name.html}"
    
10
terdon

ed

いつでもedを使用して、既存のファイルの前に行を追加できます。

$ printf '0a\n<!DOCTYPE html>\n.\nw\n' | ed my.html

細部

<!DOCTYPE html>の周りのビットは、edへのコマンドであり、その行をファイルmy.htmlに追加するように指示します。

sed

sedのこのコマンドも使用できると思います。

$ sed -i '1i<!DOCTYPE html>\n` testfile.csv
8
slm

手動で何をすることもできますPerl -iは内部で行います。

{ rm -f file && { echo '<!DOCTYPE html>'; cat; } > file;} < file

お気に入り Perl -i、バックアップはありません。ここに記載されているほとんどのソリューションと同様に、ファイルの権限と所有権に影響を与え、シンボリックリンクを通常のファイルに変える可能性があることに注意してください。

と:

sed '1i\
<!DOCTYPE html>' file 1<> file

sedはファイル自体を上書きするため、所有権、権限、シンボリックリンクには影響しません。これは、GNU sedで機能します。sedは、fileコマンドで上書きする前に、i(私の場合は4k)からデータでいっぱいのバッファーを読み取ります。ファイルがsedもその出力をバッファリングするという事実を除いて、4k以上。

基本的にsedは、読み取りと書き込みのために4kのブロックで機能します。挿入する行が4kより小さい場合、sedはまだ読み取っていないブロックを上書きすることはありません。

私はそれを当てにしません。

7

FreeBSD sed は、Mac OS Xでも使用され、-eスイッチの後に-iオプションが必要です。

言い換えれば、sed -i -e ...はFreeBSDとGNU sedの両方で機能するはずです。

より一般的には、FreeBSD sed -iの後にバックアップ拡張機能を省略するには、コマンドの解析中にFreeBSD sedの一部の混乱を避けるために、-iに続く明示的なsedオプションまたはスイッチが必要です。 -行引数。

(ただし、sedインプレースファイル編集はファイルのiノードの変更につながることに注意してください。ファイルの "インプレース"編集 を参照してください)。

(一般的なヒントとして、最近のバージョンのFreeBSD sedには-rスイッチがあり、GNU sed)との互換性が向上しています)。

echo a > testfile.txt
ls -li testfile.txt
#gsed -i -e 's/a/A/' testfile.txt
#bsdsed -i 's/a/A/' testfile.txt  # does not work
bsdsed -i -e 's/a/A/' testfile.txt
ls -li testfile.txt
cat testfile.txt
3
carlo

VimはExモードで使用できます。

ex -sc '1i|<!DOCTYPE html>' -cx file
  1. 1最初の行を選択

  2. iテキストと改行を挿入

  3. x保存して閉じる

または、コメントのように、プレーンな古い standardex

printf '%s\n' 1i '<!DOCTYPE html>' . x | ex file 
2
Steven Penny

競合状態を可能な限り回避しながら、単一のファイルに対してsed -iを移植可能にエミュレートするには、次のようにします。

sed 'script' <<FILE >file
$(cat file)
FILE

ちなみに、これはsed -iが引き起こす可能性のある問題も処理します。ディレクトリとファイルの権限に応じて、sed -iは、ユーザーが編集権限を持たないファイルを上書きできるようにする可能性があります。

次のようなバックアップも実行できます。

sed 'script' <<FILE >file
$(tee file.old <file)
FILE
2
mikeserv