web-dev-qa-db-ja.com

GNU sedとBSD / OSX sedの両方で機能するインプレース編集には、sed -iコマンドが必要です

OSXに移植しようとしているmakefile(Linuxでgmake用に開発された)がありますが、sedは協力したくないようです。私がしていることは、GCCを使用して依存関係ファイルを自動生成し、sedを使用してそれらを少し調整することです。メイクファイルの関連部分:

$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp
  $(CPPC) -MM -MD $< -o $@
  sed -i 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@

GNU/Linuxではこれは問題なく実行されますが、OSXでビルドしようとすると次のようなエラーが表示されます。

sed: 1: "test/obj/equipmentConta ...": undefined label 'est/obj/equipmentContainer_utest.d'
sed: 1: "test/obj/dice_utest.d": undefined label 'est/obj/dice_utest.d'
sed: 1: "test/obj/color-string_u ...": undefined label 'est/obj/color-string_utest.d'

Sedはキャラクターを切り落としているように見えますが、解決策がわかりません。

56
Chris Tonkinson

OS X sed は、-i引数を Linux バージョンとは異なる方法で処理します。

このように-eを追加することで、両方に対して「機能する」コマンドを生成できます。

#      vv
sed -i -e 's|\(.*\)\.o:|$(OBJ_DIR)/\1.o $(OBJ_DIR)/\1.d $(TEST_OBJ_DIR)/\1_utest.o:|' $@

OS X sed -iは、-iの後の次のものを、インプレース編集のbackup copyのファイル拡張子として解釈します。 (Linuxバージョンは、-iと拡張子の間にスペースがない場合にのみこれを行います。)明らかに、これを使用することの副作用は、拡張子として-eを含むバックアップファイルを取得することです。 。詳細と、代わりに使用できるよりクリーンなアプローチについては、この質問に対する他の回答を参照してください。

表示される動作は、OS X seds|||を拡張子(!)として消費し、next引数をコマンドとして解釈するためです。この場合は、 t。これは、sedが引数としてターゲットラベルを予期するブランチからラベルへのコマンドとして認識するため、エラーが表示されます。

ファイルtestを作成すると、エラーを再現できます。

$ sed -i 's|x|y|' test
sed: 1: "test": undefined label 'est'
64
martin clayton

実際に

sed -i -e "s/blah/blah/" files

oS Xで期待することも行いません。代わりに、「-e」拡張子の付いたバックアップファイルを作成します。

OSに適切なのは

sed -i "" -e "s/blah/blah/" files
49
Urkle

現在受け入れられている答えには、2つの非常に重要な点で欠陥があります。

  1. BSD sed(OSXバージョン)では、-eオプションはファイル拡張子として解釈されるため、-e拡張子。

  2. 提案されているdarwinカーネルのテストは、GNUまたはBSD sedが任意の数のシステムに存在する可能性があるため、クロスプラットフォームソリューションへの信頼できるアプローチではありません。

より信頼性の高いテストは、単に--versionオプションは、GNUバージョンのsedにのみあります。

sed --version >/dev/null 2>&1

Sedの正しいバージョンが決定されると、適切な構文でコマンドを実行できます。

-iオプションのGNU sed構文:

sed -i -- "$@"

-iオプションのBSD sed構文:

sed -i "" "$@"

最後に、クロスプラットフォーム関数ですべてをまとめて、インプレース編集sedコマンドを実行します。

sedi () {
    sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@"
}

使用例:

sedi 's/old/new/g' 'some_file.txt'

このソリューションは、OSX、Ubuntu、Freebsd、Cygwin、CentOS、Red Hat Enterprise、およびMsysでテストされています。

35
arctelix

これは質問に対する答えではありませんが、Linuxに相当する動作は

brew install gnu-sed

# Add to .bashrc / .zshrc
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

(以前は--with-default-namesオプションbrew install gnu-sedが、最近削除されました)

14
Anthony Sottile

私もこの問題に遭遇し、次の解決策を考えました。

darwin=false;
case "`uname`" in
  Darwin*) darwin=true ;;
esac

if $darwin; then
  sedi="/usr/bin/sed -i ''"
else
  sedi="sed -i"
fi

$sedi 's/foo/bar/' /home/foobar/bar

私のために働く;-)、YMMV

Windows、Linux、OS XでpplをビルドするマルチOSチームで働いています。一部のOS Xユーザーは、別のエラーが発生したため苦情を訴えました-GNU port of sedがインストールされていたため、フルパスを指定します。

11
thecarpy

martin claytonの役立つ答え は問題の良い説明を提供します[1]、しかし、彼が述べているように、潜在的に望ましくない副作用があるソリューション。

副作用のないソリューションは次のとおりです。

警告:以下のように-i構文問題だけを解決するだけでは十分ではないかもしれません。GNU sedとBSD/macOS sed(包括的な議論、 この答え を参照)。


-iの回避策:バックアップファイル一時的にを作成し、クリーンアップします。

空ではないサフィックス(バックアップファイルのファイル名拡張子)オプション引数(空の文字列ではない)を使用すると、can-iを使用できますBSD/macOS sedとGNU sedの両方で機能する方法。接尾辞を-iオプションに直接追加する

これを利用して、すぐにクリーンアップできるバックアップファイル一時的にを作成できます。

sed -i.bak 's/foo/bar/' file && rm file.bak

バックアップを保持したい場合は、明らかに&& rm file.bak部分を省略してください。


一時ファイルとmvを使用したPOSIX準拠の回避策:

singleファイルのみをインプレースで編集する場合、-iオプションをbypassedにすると、非互換性を回避できます。

sedスクリプトおよびその他のオプションを POSIX準拠の機能 に制限する場合、以下は完全に移植可能なソリューションです(-iは_であることに注意してください)not POSIX準拠)。

sed 's/foo/bar' file > /tmp/file.$$ && mv /tmp/file.$$ file
  • このコマンドは、変更を一時ファイルに書き込むだけで、sedコマンドが成功した場合(&&)、元のファイルを一時ファイルに置き換えます。

    • 元のファイルをバックアップとして保持する場合は、最初に元の名前を変更する別のmvコマンドを追加します。
  • 警告:基本的に、これは-iが行うことです。ただし、元のファイルの許可と拡張属性(macOS)を保持しようとする点が異なります。ただし、元のファイルがsymlinkである場合、このソリューションと-iの両方が、symlinkを通常ファイルに置き換えます。
    -iの仕組みの詳細については、 この回答 の下半分を参照してください。


[1]より詳細な説明については、 this answer of mineを参照してください。

10
mklement0

@thecarpyが投稿したソリューションを修正しました。

sed -iの適切なクロスプラットフォームソリューションは次のとおりです。

sedi() {
  case $(uname) in
    Darwin*) sedi=('-i' '') ;;
    *) sedi='-i' ;;
  esac

  LC_ALL=C sed "${sedi[@]}" "$@"
}
2
niieani