带有 -i 选项的 sed 命令在 Mac 上失败,但在 Linux 上可以运行

2024-09-30 14:02:00
admin
原创
154
摘要:问题描述:我已成功使用以下sed命令在 Linux 中搜索/替换文本:sed -i 's/old_link/new_link/g' * 然而,当我在 Mac OS X 上尝试时,我得到:“命令 c 需要 后跟文本”我以为我的 Mac 运行的是普通的 BASH shell。...

问题描述:

我已成功使用以下sed命令在 Linux 中搜索/替换文本:

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

然而,当我在 Mac OS X 上尝试时,我得到:

“命令 c 需要 后跟文本”

我以为我的 Mac 运行的是普通的 BASH shell。怎么回事?

编辑:

根据@High Performance,这是由于 Macsed具有不同的(BSD)风格,所以我的问题是如何在 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

GNU 期望将扩展名立即放在-i(例如-i''-i'.bak',没有空格)之后,但 macOS 期望在(例如或)sed之后有一个空格。-i`-i ''`-i '.bak'

并且现在也被 Mac (BSD) 所接受sed,尽管早期版本并不接受它(例如在 Mac OS X v10.6 中,后面需要-i一个空格,例如-i '.bak')。

-e参数允许我们明确说明声明编辑命令的位置。

直到 2013 年 Mac OS 更新之前,

仍然没有任何跨 GNU 和 Mac(BSD)的可移植命令,因为这些变体全部失败(出现错误或意外的备份文件):

  • sed -i -e ...- 适用于 Linux,但不适用于 macOS,因为它会创建-e备份

  • sed -i '' -e ...- 在 macOS 上有效,但在 Linux 上失败

  • sed -i='' -e ...- 在 macOS 和 Linux 上创建=备份文件

  • sed -i'' -e ...- 在 macOS 上创建-e备份文件并''在 Linux 上备份

便携式解决方案

您可以通过几种方式在 Linux 和 macOS 上实现相同的结果,例如:

  1. 使用 Perl perl -i -pe's/old_link/new_link/g' *:。

  2. gnu-sed在 macOS 上使用(使用Homebrew安装)

# 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 内置版本。gsed`sedsedsed`

如果您正在使用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

解决方案 2:

我相信在 OS X 上使用 -i 时需要备份文件的扩展名尝试:

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

使用 GNUsed扩展是可选的

解决方案 3:

在 Mac 中遇到了同样的问题,并使用以下方法解决了brew

brew install gnu-sed

并使用

gsed SED_COMMAND

您也可以将其设置sed为别名gsed(如果您愿意):

alias sed=gsed

解决方案 4:

这适用于 GNU 和 BSD 版本的 sed:

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

或备份:

sed -i'.bak' -e 's/old_link/new_link/g' *

注意选项后缺少空格-i!(GNU sed 必需)

解决方案 5:

或者,您可以在 Mac 中安装 sed 的 GNU 版本(称为 gsed),并使用标准 Linux 语法使用它。

为此,请通过运行gsed来使用 ports 安装(如果您没有,请从<http://www.macports.org/>获取)sudo port install gsed。然后,您可以运行sed -i &#039;s/old_link/new_link/g&#039; *

解决方案 6:

您的 Mac 确实运行 BASH shell,但这更多的是您正在处理的 sed 的哪个实现的问题。在 Mac 上,sed 来自 BSD,与您在典型的 Linux 机器上看到的 sed 略有不同。我建议您man sed

解决方案 7:

Sinetris 的答案是正确的,但我使用这个find命令来更具体地说明我想要更改哪些文件。一般来说,这应该有效(在 osx 上测试过/bin/bash):

find . -name &quot;*.smth&quot; -exec sed -i &#039;&#039; &#039;s/text1/text2/g&#039; {} ;

一般来说,在复杂的项目中sed不使用时find效率较低。

解决方案 8:

sed我不会用 来调用 sed./bin/sed

这是我的包装脚本~/project/bin/sed

#!/bin/bash

if [[ &quot;$OSTYPE&quot; == &quot;darwin&quot;* ]]; then
  exec &quot;gsed&quot; &quot;$@&quot;
else
  exec &quot;sed&quot; &quot;$@&quot;
fi

不要忘记chmod 755包装脚本。

解决方案 9:

我创建了一个函数来处理sedMacOS(在 MacOS 10.12 上测试)和其他操作系统之间的差异:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
    if [ &quot;$OS&quot; = &#039;Darwin&#039; ]; then
        # for MacOS
        sed -i &#039;&#039; -e &quot;$1&quot; &quot;$2&quot;
    else
        # for Linux and Windows
        sed -i&#039;&#039; -e &quot;$1&quot; &quot;$2&quot;
    fi
}

用法:

$(replace_in_file &#039;s,MASTER_HOST.*,MASTER_HOST=&#039;&quot;$MASTER_IP&quot;&#039;,&#039; &quot;./mysql/.env&quot;)

在哪里:

,是分隔符

&#039;s,MASTER_HOST.*,MASTER_HOST=&#039;&quot;$MASTER_IP&quot;&#039;,&#039;是模式

&quot;./mysql/.env&quot;是文件路径

解决方案 10:

这是 Bash 脚本中的一个选项:

#!/bin/bash

GO_OS=${GO_OS:-&quot;linux&quot;}

function detect_os {
    # Detect the OS name
    case &quot;$(uname -s)&quot; in
      Darwin)
        host_os=darwin
        ;;
      Linux)
        host_os=linux
        ;;
      *)
        echo &quot;Unsupported host OS. Must be Linux or Mac OS X.&quot; >&amp;2
        exit 1
        ;;
    esac

   GO_OS=&quot;${host_os}&quot;
}

detect_os

if [ &quot;${GO_OS}&quot; == &quot;darwin&quot; ]; then
    sed -i &#039;&#039; -e ...
else
    sed -i -e ...
fi

解决方案 11:

正如其他答案所指出的那样,没有办法在不制作备份文件的情况下在 OS X 和 Linux 之间移植使用 sed。因此,我改用这个 Ruby 单行代码来执行此操作:

ruby -pi -e &quot;sub(/ $/, &#039;&#039;)&quot; ./config/locales/*.yml

就我而言,我需要从任务中调用它rake(即在 Ruby 脚本内部),因此我使用了这个额外的引用级别:

sh %q{ruby -pi -e &quot;sub(/ $/, &#039;&#039;)&quot; ./config/locales/*.yml}

解决方案 12:

以下是如何将环境变量应用于模板文件(无需备份)。

1. 创建带有{{FOO}}的模板以供稍后替换。

echo &quot;Hello {{FOO}}&quot; > foo.conf.tmpl

2. 将 {{FOO}} 替换为 FOO 变量并输出到新的 foo.conf 文件

FOO=&quot;world&quot; &amp;&amp; sed -e &quot;s/{{FOO}}/$FOO/g&quot; foo.conf.tmpl > foo.conf

适用于macOS 10.12.4Ubuntu 14.04.5

解决方案 13:

我找到了一个兼容度更高的可移植命令版本sed,它既适用于 Linux,也适用于 Mac。在某些情况下,例如在package.json scripts部分中使用,该命令-i&#039;&#039;在 Mac 上甚至在 Sonoma 版本中也莫名其妙地停止工作,它会抛出错误消息,例如“d 命令末尾有多余的字符”。

诀窍是使用-i=&#039;&#039;而不是-i &#039;&#039;-i&#039;&#039;-i。这是一个简单的例子:

echo &#039;ABCD1234&#039; > /tmp/test.txt
sed -i=&#039;&#039; -e &#039;s/ABCD/DCBA/g&#039; /tmp/test.txt
cat /tmp/test.txt

Linux 和 Mac 的输出相同DCBA1234

解决方案 14:

另一种可移植的方法是使用 Vim 的Ex 模式 ex

ex -sc &#039;%s/old/new/g&#039; -c &#039;x&#039; file
  1. -s静音模式

  2. -c运行命令

我们传递两个命令:

  1. -c &#039;%s/old/new/g&#039;

* `%`选择所有行
* `s`代替
* `/old/new/`
* `g`替换所有出现的情况
  1. -c &#039;x&#039;

* `x`保存并退出

请注意,如果文件中不存在该字符串,则简单地传递|x该字符串会导致编辑器挂起:&#039;%s/old/new/g|x&#039;

Error detected while processing command line:
E486: Pattern not found: old

使用附加功能-c &#039;x&#039;可以解决该问题。

大多数人都希望在 find 命令中找到它,因此这里有一个神奇的咒语:

find . \n  -type f \n  -exec ex -sc &#039;%s/old/new/g&#039; -c &#039;x&#039; {} ;

PS:不要像我一样弄乱你的 git 文件夹:

find . \n  -path ./.git -prune -o \n  -type f \n  -exec ex -sc &#039;%s/old/new/g&#039; -c &#039;x&#039; {} ;

解决方案 15:

sed -ie &#039;s/old_link/new_link/g&#039; *

使用 gnu sed 可在 BSD 和 Linux 上运行

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用