-i オプション付きの sed コマンドは Mac では失敗しますが、Linux では動作します。質問する

-i オプション付きの sed コマンドは Mac では失敗しますが、Linux では動作します。質問する

sedLinux でテキストを検索/置換するために次のコマンドを正常に使用しました。

sed -i 's/old_link/new_link/g' *

しかし、Mac OS X で試してみると、次のようになります:

「コマンド c は \ の後にテキストが続くことを期待します」

私の Mac では通常の BASH シェルが実行されると思っていました。どうなっているのでしょうか?

編集:

@High Performance によると、これは Mac が異なる (BSD) フレーバーであるためですsed。したがって、私の質問は、このコマンドを BSD で複製するにはどうすればよいかということですsed

編集:

これを引き起こす実際の例を以下に示します。

sed -i 's/hello/gbye/g' *

ベストアンサー1

オプション-i(または--in-place) は、変更を新しい場所にストリーミングするのではなく、ファイルをその場で編集することを意味します。

ファイルをその場で変更する場合は、バックアップ ファイルが必要であることが示唆されます。そのため、 の後にユーザー指定の拡張子が期待されます-iが、拡張子引数の解析は GNU sed と Mac (BSD) sed では異なる方法で処理されます。

  • GNU: 「拡張子が指定されていない場合、バックアップを作成せずに元のファイルが上書きされます。」 - 実質的に、ファイル拡張子の指定を完全に省略できます。拡張子は、 の直後に、間にスペースを入れずに指定する必要があります-i
  • Mac (BSD): 「長さがゼロの拡張子が指定された場合、バックアップは保存されません。」 -拡張子を指定する必要がありますが、必要に応じて空の文字列 '' を指定してバックアップを無効にすることもできます。

したがって、GNU と Mac では、これを異なる方法で解釈します。

sed -i 's/hello/bye/g' just_a_file.txt
  • GNU : の直後に拡張子が指定されていない-iため、バックアップは作成されず、s/hello/bye/gテキスト編集コマンドとして使用され、just_a_file.txtその場でファイルに対して処理が行われます。
  • Mac (BSD) :s/hello/bye/gバックアップ ファイル拡張子 (!) を使用し、just_a_file.txtテキスト編集コマンドとして使用しますが、あーあ!: そこに指定されているコマンド コードはj(たとえば、s置換用の有効なコマンド コードではない) なので、 でエラーが発生しますinvalid command code j
# This still create a `my_file.txt-e` backup on macOS Sonoma (14.5)
# and a `my_file.txt''` on Linux
sed -i'' -e 's/hello/bye/g' my_file.txt

拡張子を の直後に配置すること-i(例:-i''または-i'.bak'、スペースなし) は GNUsedでは想定されていますが、macOS では の後にスペースがあること-i(例-i '': または-i '.bak') が想定されています。

および は、以前のバージョンでは許容されていませんでしたが (たとえば、Mac OS X v10.6 では、の後に sed スペースが 必要でし た。 -i 例: ) 、現在では Mac (BSD) でも受け入れられ ます。 -i '.bak'

この-eパラメータを使用すると、編集コマンドを宣言する場所を明示的に指定できます。

2013年にMac OSがアップデートされるまでは、

ただし、GNU と Mac (BSD) 間で移植可能なコマンドは存在せず、これらのバリエーションはすべて失敗しました (エラーまたは予期しないバックアップ ファイルで)。

  • sed -i -e ...-e- Linuxでは動作しますが、バックアップを作成するためmacOSでは動作しません。
  • sed -i '' -e ...- macOSでは動作しますが、Linuxでは動作しません
  • sed -i'' -e ...- -emacOSでバックアップファイルを作成し、''Linuxでバックアップする

ポータブルソリューション

Linux と macOS で同じ結果を得るには、次のようないくつかのオプションがあります。

  1. Perl を使用します: perl -i -pe's/old_link/new_link/g' *

  2. gnu-sedmacOSでの使用(インストールには自家製

# Install 'gnu-sed' on macOS using Homebrew
brew install gnu-sed
# Use 'gsed' instead of 'sed' on macOS.
gsed -i'' -e 's/hello/bye/g' my_file.txt

注:gnu-sed macOS では、コマンドを含むbin パスを PATH 環境変数に追加するか、 assedのエイリアスを作成することができます(macOS をGNU に置き換えます)。 macOS 組み込みバージョンに依存するスクリプトが存在する可能性があるため、これを行わないことをお勧めします。gsedsedsedsed

sedスクリプトで を使用している場合は、次の切り替えを自動化することができますgsed

#!/usr/bin/env bash
set -Eeuo pipefail

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Require gnu-sed.
  if ! [ -x "$(command -v gsed)" ]; then
    echo "Error: 'gsed' is not istalled." >&2
    echo "If you are using Homebrew, install with 'brew install gnu-sed'." >&2
    exit 1
  fi
  SED_CMD=gsed
else
  SED_CMD=sed
fi

# Use '${SED_CMD}' instead of 'sed'
${SED_CMD} -i'' -e 's/hello/bye/g' my_file.txt
  1. -i ''macOSとBSDで使用するか、-iそれ以外の場合は(GNU sed)を使用する
#!/usr/bin/env bash
set -Eeuo pipefail

case "$OSTYPE" in
  darwin*|bsd*)
    echo "Using BSD sed style"
    sed_no_backup=( -i '' )
    ;; 
  *)
    echo "Using GNU sed style"
    sed_no_backup=( -i )
    ;;
esac

sed "${sed_no_backup[@]}" -e 's/hello/bye/g' my_file.txt

おすすめ記事