使用 nix-shell 或 nix shell 在 Linux 64 上运行 MacOS sed

Posted

技术标签:

【中文标题】使用 nix-shell 或 nix shell 在 Linux 64 上运行 MacOS sed【英文标题】:Run MacOS sed on Linux 64 using nix-shell or nix shell 【发布时间】:2021-12-20 03:10:34 【问题描述】:

回答关于 SO 的 sed 问题我经常遇到“适用于 linux 但不适用于 MacOS”的问题,是否可以将在 MacOS 上运行的 sed 版本加载到 nix shell 中?

目前使用 flakes,我可以使用以下方法获取最新的 gnused:

nix shell nixpkgs#gnused

有没有办法临时安装 BSD/MacOS 版本的 sed?

【问题讨论】:

使用 --posix 运行 gnu sed 我不认为在 Nixpkgs 中打包了另一个 sed 变体。可能没有必要。 你是potong - 你不能关于sed的问题!!!苹果sed的源码在这里,如果你想尝试编译它...opensource.apple.com/source/text_cmds/text_cmds-106/sed 我通过在我当前的 macOS 上运行 strings $(which sed) 得到了这一点(尽管他们的 sed 在 12 年内没有改变)并且它在顶部说这个 @(#)PROGRAM:sed PROJECT:text_cmds-106 未经测试,但 Dex 提供了一种“将 macos-sed 安装到 /usr/local/bin,带有 'macos-' 前缀”的方法,例如echo "ping" | macos-sed 's/ping/pong/'。可能值得一看。 【参考方案1】:

不久前我研究了这个问题;也许你可以利用 我发现了什么。

Web 搜索显示在 BSD 之外对 FreeBSD sed 的需求很少,所以我 决定从 github 并在最近的 Debian 系统上构建和测试它。为此,我创建了 3 个文件(如下所列):

adapt/local.c - 支持非 GNU 函数 adapt/local.h - 同上头文件 Makefile - 下载、生成所需文件、运行 1 或 2 测试套件;参考标头和目标附近的 cmets test

在我的系统上,可执行文件的构建没有错误。测试它使用 tests/multi_test.sh我跑了

make test | tee multi_test.log | grep '^not ok' | tee multi_test.err.log

130 次测试中有 127 次成功,3 次失败:

not ok 69 7.1 # Print and file routines
not ok 75 7.7 # w results
not ok 97 8.21 # \ in y command

我认为,这些差异不会超出预期:#69 由sed -n l 的输出差异触发,#75 由差异触发 在/usr/share/dict/words 内容中(便携性有限的测试用例), 如果在 makefile 中使用 SHELL := /bin/bash 或使用 #97 则会消失 测试脚本中的 printf '%s\n' 'a\b(c' 而不是 echo 'a\b(c'更新 2021-11-24:#69 消失,例如 LANG=en make test | grep '^not ok',导致依赖于语言环境 库函数iswprint()process.c#lputs() 中为字符 0xA0..0xFF 返回零。 #75 只有/usr/share/dict/words 的前 200 行是 与测试用例作者使用的相同;我看不到 这背后的推理。 (结束更新

还在待办事项清单上:

Makefile*.names 变量是硬编码的,更好地创建 它们是动态的;然而,名称似乎很少改变 带有sed 历史的程序 支持ATF tests/sed2_test.sh 的测试(目前被忽略) 支持在可执行文件中嵌入标识字符串 (__FBSDID 宏当前被忽略)而不是依赖于手册页中的日期 version info

./Makefile

注意这里的食谱没有以通常的制表符作为前缀 但>(在行首)充当make 运算符 (.RECIPEPREFIX = >)。

# desc:
#   Download, build, test FreeBSD sed (dated 2020-06-10) on GNU/Linux
# compat:
#   dash 0.5.10  GNU make 4.2.1  GNU wget 1.20  GNU gcc 9.3.0  man 2.9
# ref:
#   https://www.freebsd.org/cgi/man.cgi?sed
#   https://github.com/freebsd/freebsd-src/tree/main/usr.bin/sed
#   https://github.com/joshuarubin/wcwidth9
# files:
#   Makefile adapt/local.c adapt/local.h
# howto:
#   make download
#   make all
#   (optional) make download.tests download.regress.multitest.out test
#   (optional) cp $(exe) /usr/local/bin/bsdsed
#   (optional) cp $(man.1) /usr/local/man/man1/bsdsed.1
# note:
#   Mind the $(wgetFlags) and $(CFLAGS)

SHELL       := /bin/sh
wgetFlags   ?= --no-verbose --wait=1
# $(call wgetCmd,subtarget-name)
define wgetCmd =
  wget $(wgetFlags) --no-host-directories --directory-prefix=$($1.ldir) \
  -- $(addprefix $($1.url),$($1.names))
endef
#
hdrs        := defs.h extern.h
srcs        := misc.c compile.c process.c main.c
hdrx        := wcwidth9.h
srcx        := local.c
objs        := $(patsubst %.c,%.o,$(srcs) $(srcx))
# test scripts expect an executable named 'sed'
exe         := sed
man.1       := $(exe).1
man.ps      := $(exe).ps
binaries    := $(exe) $(man.1) $(man.ps)
#
subtargets  := wcwidth9 sed tests regress.multitest.out
dnldtargets := $(addprefix download.,$(subtargets))
#
wcwidth9.url    := https://github.com/joshuarubin/wcwidth9/raw/master/
wcwidth9.names  := $(hdrx)
wcwidth9.ldir   := ./
#
sed.url     := https://github.com/freebsd/freebsd-src/raw/master/usr.bin/sed/
sed.names   := POSIX sed.1 $(srcs) $(hdrs)
sed.ldir    := ./
tests.url   := $(sed.url)tests/
tests.names := \
    hanoi.sed inplace_race_test.sh legacy_test.sh math.sed  \
    multi_test.sh regress.G.out regress.P.out regress.b2a.out  \
    regress.bcb.out regress.c0.out regress.c1.out regress.c2.out  \
    regress.c3.out regress.hanoi.out regress.icase1.out  \
    regress.icase2.out regress.icase3.out regress.icase4.out  \
    regress.in regress.math.out regress.not.out regress.psl.out  \
    regress.s3.out regress.s4.out regress.s5.out regress.sg.out  \
    regress.sh regress.y.out sed2_test.sh
tests.ldir  := ./tests/
regress.multitest.out.url   := $(sed.url)tests/regress.multitest.out/
regress.multitest.out.names := \
    1.1 1.2 1.3 1.4 1.4.1 1.5 1.6 1.7 1.8 1.9 1.10 1.11 1.12 1.13  \
    1.14 1.15 1.16 1.17 1.18 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9  \
    2.10 2.11 2.12 2.13 2.14 2.15 2.16 2.17 2.18 2.19 2.20 2.21  \
    2.22 2.23 3.1 3.2 3.3 3.4 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 5.1  \
    5.2 5.3 5.4 5.5 5.6 5.7 5.8 6.1 6.2 6.3 6.4 6.5 6.6 7.1 7.2  \
    7.3 7.4 7.5 7.6 7.7 7.8 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9  \
    8.10 8.11 8.12 8.13 8.14 8.15 8.16 8.17 8.18 8.19 8.20 8.21  \
    8.22 8.23 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 9.10 9.11 9.12  \
    9.13 9.14 9.15 9.16 9.17 9.18 9.19 9.20 9.21 9.22 9.23 9.24  \
    9.25 9.26 9.27 9.28 9.29 9.30 9.31
regress.multitest.out.ldir  := ./tests/regress.multitest.out/


.RECIPEPREFIX = >
.DELETE_ON_ERROR:
.PHONY: all clean realclean
all : $(binaries)
clean : ; rm -f -- $(objs) $(binaries)
realclean : clean
> rm -f -- local.c manpage.1 $(foreach T,$(subtargets),$(addprefix $($(T).ldir),$($(T).names)))
> rmdir --ignore-fail-on-non-empty -- \
        $(patsubst %/,%,$(foreach T,$(subtargets),$(filter-out %. %./,$($(T).ldir))))


# notonbsd: enable modifications in extern.h and $(srcx)
# __FBSDID: don't embed RCS ID
$(objs) : CFLAGS += -Dnotonbsd -D__FBSDID\(s\)=
$(objs) : $(srcs) $(hdrs)
extern.h : adapt/local.h
> grep -q '^.ifdef\s*notonbsd' $@ ||  cat $@ $< > $@.tmp && mv -f -- $@.tmp $@ ; 
local.c : adapt/local.c
> cp $< $@
local.o : $(hdrx)
main.o : CFLAGS += -D__unreachable=__builtin_unreachable

$(exe) : $(objs)
> $(LINK.c) -o $@ $^
> $(if $(DEBUG),,strip $@)

$(man.1) : manpage.1
> cp $< $@

%.ps : %.1
> man -l -t $< > $@


.PHONY : download.all $(dnldtargets) download
download.all : $(dnldtargets)
$(dnldtargets) :
> $(call wgetCmd,$(patsubst download.%,%,$@))

download : download.wcwidth9 download.sed
> mv -f sed.1 manpage.1
> touch adapt/local.h


.PHONY: test
# ! run after: make download.tests download.regress.multitest.out
# ! hint: make test | tee multi_test.log | grep '^not ok' | tee multi_test.err.log
# - set PATH so scripts invoke the new sed executable
# - run multi_test.sh (requires /usr/share/dict/words regress.multitest.out/*)
# - optionally run inplace_race_test.sh
# - ignore legacy_test.sh regress.* (require m4 regress.m4 hanoi.sed math.sed)
# - ignore sed2_test.sh (requires ATF, cf. https://github.com/freebsd/freebsd-src/tree/main/contrib/atf)
test : | /usr/share/dict/words
> cd $(patsubst %/,%,$(tests.ldir)); \
    PATH="..:$$PATH"; \
    $(SHELL) multi_test.sh \
    $(if $(racetest),; $(SHELL) inplace_race_test.sh && rm -f file[0-9] file[0-9].prev)

./adapt/local.c

/* Support for non-GNU functions, cf. man.freebsd.org */
#ifdef notonbsd

#include <err.h>
#include <limits.h>
#include <regex.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#include <stdarg.h>
#include <errno.h>

#include "defs.h"
#include "extern.h"
#include "wcwidth9.h"  /* https://github.com/joshuarubin/wcwidth9 */

void
errc(int eval, int code, const char *fmt, ...)

    va_list args;

    va_start(args, fmt);
    errno = code;
    err(eval, fmt, args);
    va_end(args);


char*
getprogname()

    return (program_invocation_short_name);


int
wcwidth(wchar_t wc)

    return wcwidth9(wc);


size_t
strlcpy(char *dst, const char *src, size_t dstsize)

    return snprintf(dst, dstsize, "%s", src);


size_t
strlcat(char *dst, const char *src, size_t dstsize)

    int dlen, slen, dslen, addlim;
    dlen = strlen(dst);
    slen = strlen(src);
    dslen = dlen + slen;
    addlim = (dstsize > dslen ? slen : dstsize - dlen - 1);
    if ( addlim > 0 )
        strncat(dst, src, addlim);
    return dslen;


#endif  /* notonbsd */

./adapt/local.h

/* To be appended to extern.h */
#ifdef notonbsd
extern  char *program_invocation_short_name;
char    *getprogname();
void    errc(int eval, int code, const char *fmt, ...);
int     wcwidth(wchar_t wc);
size_t  strlcpy(char *dst, const char *src, size_t dstsize);
size_t  strlcat(char *dst, const char *src, size_t dstsize);
#endif  /* notonbsd */


更新于 2022-01-07

按照 Makefile 在 Debian 系统上构建 macOS sed 使用 与上面列出的 ./adapt/local.c./adapt/local.h 相同。在我的尽头 make download all 构建可执行文件没有错误,但警告 如果$(CFLAGS) 包含-O3,则忽略返回值fchown

make download.TEST test 下载测试文件,生成并运行 test-edited.sh(修改后的TEST/sed.test)调整测试 参数并修复重定向问题。测试日志显示 113 115 个 (30+85) 个测试用例中的 (30+83) 个成功(test_error() 中的 30 个, 其余编号):

测试 2.8 失败:sed -n -e '0p' lines1 由 macOS sed 运行(和 FreeBSD sed) 但 GNU sed 说“行地址 0 的使用无效” 测试 7.1 失败,即使行继续 char.s 被消除: macOS 的l 命令sed(和FreeBSD sed)将反斜杠输出为\ 但是POSIX.1-2017 (和 GNU sed)说 \\

测试 7.7(涉及/usr/share/dict/words)表明 应该运行等效的 FreeBSD sed testcase #75(见上文) 针对本地文件,而不是测试作者生成的文件。


./Makefile

注意:配方前缀是&gt;(在行首),而不是制表符。

# desc:
#   Download, build, test macOS sed (dated 2017-03-27) on GNU/Linux.
# compat:
#   dash 0.5.10  GNU make 4.2.1  GNU wget 1.20.3  GNU gcc 9.3.0 
#   GNU sed 4.7  GNU coreutils 8.30  man 2.9.1
# ref:
#   https://opensource.apple.com/source/text_cmds/text_cmds-106/sed/
#   https://github.com/joshuarubin/wcwidth9
# see:
#   Last sed manpage (dated May 10, 2005) captured by archive.org on Aug 8, 2017
#   https://web.archive.org/web/20170808213955/https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/sed.1.html
#   https://leancrew.com/all-this/2021/03/apple-and-links/
#   https://opensource.apple.com/source/text_cmds/text_cmds-106/text_cmds.plist
#   https://unix.stackexchange.com/questions/13711/differences-between-sed-on-mac-osx-and-other-standard-sed
# files:
#   Makefile adapt/local.c adapt/local.h
# howto:
#   make download
#   make all
#   (optional) make download.TEST test
# note:
#   Mind the $(wgetFlags) and $(CFLAGS)

SHELL       := /bin/sh
refsed      ?= /usr/bin/sed --posix
wgetFlags   ?= --no-verbose --wait=1
# $(call wgetCmd,subtarget-name)
define wgetCmd =
  wget $(wgetFlags) --no-host-directories --directory-prefix=$($1.ldir) \
  -- $(addprefix $($1.url),$($1.names))
endef
#
hdrs        := defs.h extern.h
srcs        := misc.c compile.c process.c main.c
hdrx        := wcwidth9.h
srcx        := local.c
objs        := $(patsubst %.c,%.o,$(srcs) $(srcx))
exe         := sed
#
sed.genfiles    := $(exe) $(exe).1.gz $(exe).pdf
test.gendirs    := ./sed.out/ ./nsed.out/
test.genfiles   := test-edited.sh test.log test-diff.log $(addsuffix *,$(test.gendirs))
#
subtargets  := wcwidth9 sed TEST
dnldtargets := $(addprefix download.,$(subtargets))
#
wcwidth9.url    := https://github.com/joshuarubin/wcwidth9/raw/master/
wcwidth9.names  := $(hdrx)
wcwidth9.ldir   := ./
sed.url     := https://opensource.apple.com/source/text_cmds/text_cmds-106/sed/
sed.names   := POSIX sed.1 $(srcs) $(hdrs)
sed.ldir    := ./
TEST.url    := $(sed.url)TEST/
TEST.names  := hanoi.sed math.sed sed.test
TEST.ldir   := ./TEST/


.RECIPEPREFIX = >
.DELETE_ON_ERROR:

.PHONY: all testclean clean realclean
all : $(sed.genfiles)
testclean :
> rm -f -d -- $(test.genfiles) $(patsubst %/,%,$(test.gendirs))
clean : testclean
> rm -f -- $(objs) $(sed.genfiles)
realclean : clean
> rm -f -- local.c $(foreach T,$(subtargets),$(addprefix $($(T).ldir),$($(T).names)))
> rmdir --ignore-fail-on-non-empty -- $(patsubst %/,%, \
    $(foreach T,$(subtargets),$(filter-out %. %./,$($(T).ldir))))


# notonbsd: enable modifications in extern.h and $(srcx)
# __FBSDID: don't embed RCS ID
$(objs) : CFLAGS += $(if $(DEBUG),,-s -O3) -Dnotonbsd -D__FBSDID\(s\)=
$(objs) : $(srcs) $(hdrs)
extern.h : adapt/local.h
> grep -q '^.ifdef\s*notonbsd' $@ ||  cat $@ $< > $@.tmp && mv -f -- $@.tmp $@ ; 
local.c : adapt/local.c
> cp $< $@
local.o : $(hdrx)
main.o : CFLAGS += -D__unreachable=__builtin_unreachable

$(exe) : $(objs)
> $(LINK.c) $^ -o $@

%.1.gz : %.1
> gzip -fk $<

%.ps : %.1
> man -l -t $< > $@

%.pdf : %.ps
> ps2pdf $< $@

.PHONY : download.all $(dnldtargets) download
download.all : $(dnldtargets)
$(dnldtargets) :
> $(call wgetCmd,$(patsubst download.%,%,$@))

download : download.wcwidth9 download.sed
> touch adapt/local.h


.PHONY: test
# run after: make download.TEST
# notes:
#   test 2.8: GNU sed says "invalid usage of line address 0"
#   test 7.1 fails, even when line continuation char.s are eliminated:
#       `l` command of macOS sed (and BSD sed) outputs backslash as '\'
#       but POSIX.1-2017 (and GNU sed) says `\\`
test : test-diff.log
> printf '## test results in "%s"\n' '$<' 1>&2

test-diff.log : test.log
> - diff -c $(test.gendirs) > $@

# notes:
#   `LANG=en`: cause test 7.1 not to output unicode/widechar
#   creates $(test.gendirs)
#   all tests in test_error() are supposed to produce error output
test.log : test-edited.sh
> LANG=en $(SHELL) $< 1>$@ 2>&1
> rm -f -- lines[1-4] script[1-2]

test-edited.sh : $(TEST.ldir)sed.test $(exe)
> $(refsed) \
    -e '# main() : adjust test parameters' \
    -e '/^.BASE=/ s,.*,BASE="$(refsed)",' \
    -e '/^.TEST=/ s,.*,TEST="./$(exe)",' \
    -e '# be silent' \
    -e '/^.BSD=/ s,.*,BSD=0,' \
    -e '/^.GNU=/ s,.*,GNU=0,' \
    -e '/^.SUN=/ s,.*,SUN=0,' \
    -e '# allow "sed --posix" as exename' \
    -e '/^.tests / s,\(\$$[A-Z][A-Z]*\),"\1",g' \
    -e '# comment out "diff -c"' \
    -e '/^.diff -c/ s,^,#,' \
    -e '# test_error(): fix stdin redirection' \
    -e '/^.exec 0>&3 4>&1 5>&2/ s,0>&3,3<\&0,' \
    -e '/^.exec 0>&3 1>&4 2>&5/ s,0>&3,0<\&3,' \
    $< > $@

.PHONY : test-7.1-compare
# special case
test-7.1-compare : $(addsuffix .nolinecontinuation,$(wildcard $(addsuffix *_7.1,$(test.gendirs))))
> - diff -c $^

%.nolinecontinuation : %
> $(refsed) -e ':a' -e '/[^\\]\\$$/N; s/\\\n//; ta' < $< > $@

可能感兴趣:

$ apt-cache search '^freebsd'
ctfutils - FreeBSD CTF utilities
freebsd-buildutils - Utilities for building FreeBSD sources
freebsd-glue - Emulate a FreeBSD build environment
freebsd-manpages - Manual pages for a GNU/kFreeBSD system
freebsd-mk - FreeBSD makefile templates for bmake
libarchive-tools - FreeBSD implementations of 'tar' and 'cpio' and other archive tools
libfreebsd-glue-0 - FreeBSD glue environment (shared objects)
libipx2 - FreeBSD IPX address conversion support library
libsbuf6 - FreeBSD string buffer library
libutil-freebsd-9 - FreeBSD utility library

【讨论】:

谢谢。我也许可以用它来构建一个 nix 衍生产品。 @potong:已经有一段时间了,但我现在已经为 macOS 版本添加了一个 GNU makefile。 再次感谢,我会尝试将其改编为 nix 派生。

以上是关于使用 nix-shell 或 nix shell 在 Linux 64 上运行 MacOS sed的主要内容,如果未能解决你的问题,请参考以下文章

我可以复制nix-build与nix-shell和cabal构建的内容吗?

如何使用 cabal 在 ghcid 中运行测试套件?

清理丑陋的所见即所得 HTML 代码? Python 或 *nix 实用程序

*nix Eclipse Shell 编辑器

在 nix 上的 haskell 开发环境中使用 hoogle

*nix 伪终端如何工作?啥是主/从通道?