如何通过 sh 将带有 $* 的空格的文件名从 xargs 正确传递到 sed?

How to properly pass filenames with spaces with $* from xargs to sed via sh?

免责声明:这发生在 macOS (Big Sur) 上;有关以下上下文的更多信息。

我必须(几乎已经)编写了一个脚本,它将大文本 (xml) 文件中的图像 URL 替换为其 Base64 编码值。

脚本应该运行与单个文件名或模式或两者相同的方式,例如:

./replace-encode single.xml
./replace-encode pattern*.xml
./replace-encode single.xml pattern*.xml
./replace-encode folder/*.xml

注意:它应该正确处理 files\ with\ spaces.xml

所以我得到了这个脚本:

#!/bin/bash

#needed for `ls` command
IFS=$'\n'

ls -1 $* | xargs -I % sed -nr 's/.*>(https?:\/\/[^<]+)<.*//p' % | xargs -tI % sh -c 'sed -i "" "s@%@`curl -s % | base64`@" [=11=]' "$*"

它的作用:ls 所有文件,将列表通过管道传送到 xargs,然后搜索所有被锚包围的 URL(因此搜索中的 >< expr。 - 还必须使用 sed 因为 grep 在 macOS 上受限),然后再次通过管道传输到 sh 脚本,该脚本 运行 是 sed 搜索 &替换,其中替换为大 Base64 字符串。

这很好用...但仅适用于 fileswithoutspaces.xml

我试着玩 [=23=] vs </code>, <code>$* vs $@, w/ or w/o " 但没有有用

我不明白变量替换到底是怎么回事(是这样叫的吗?-我不是以英语为母语的人,最重要的是,我根本不是编剧!!!只是 Java dev. all day long...) 在 xargssh 甚至 bash 之间工作,使用文件名等参数。

xargs -t 在这里让我检查替换是如何工作的,这就是我注意到使用模式有效但我必须让 " 在最后一个 [=25] 附近=], 否则只搜索并替换第一个文件;输出如下:

user@host % ./replace-encode pattern*.xml
sh -c sed -i "" "s@https://www.some.com/public/123456.jpg@`curl -s https://www.some.com/public/123456.jpg | base64`@" [=12=] pattern_123.xml
pattern_456.xml

pattern_123.xmlpattern_456.xml都在这里处理;在命令末尾使用 $* 而不是 "$*",仅处理 pattern_123.xml

那么有没有一种简单的方法可以“解决”这个问题?

谢谢。

注意:macOS 命令有一些限制(我知道),但由于此脚本是为非技术用户准备的,我不能要求他们安装(或让 IT 团队代表他们安装)一些备用 GNU -安装的版本,例如pcregrep 或 'ggrep' 就像我读过很多次...

另外:我不打算将循环从 xargs 更改为 for 左右,因为,1/ 没有时间,2/ 可能想优化第二步,其中有些网址可能重复。

您的软件没有理由使用 lsxargs,更不用说 $*

./replace-encode single.xml
./replace-encode pattern*.xml
./replace-encode single.xml pattern*.xml
./replace-encode folder/*.xml

...都可以正常工作:

#!/usr/bin/env bash
while IFS= read -r line; do
  replacement=$(curl -s "$line" | base64)
  in="$line" out="$replacement" perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' "$@"
done < <(sed -nr 's/.*>(https?:\/\/[^<]+)<.*//p' "$@" | sort | uniq)

最终得到这个 single-line 脚本:

sed -nr 's/.*>(https?:\/\/[^<]+)<.*//p' "$@" | xargs -I% sh -c 'sed -i "" "s@%@`curl -s % | base64`@" "$@"' _ "$@"

它正确地支持带或不带空格的文件名。