用sed替换环境变量

environment variables substitution with sed

我想替换以下输入(在 HTML 页面中):

<base href="" />

<base href="http://mywebsite.com/image/" /> 用于不同的文件。

这就是我正在做的。对于每个文件,我们得到 <base 标签所在的文件行。

nb_ligne=$(grep -n '<base' $i  | awk -F : '{print }')

我们在这里删除当前目录上面的第一个目录。

path_dir=$(echo $i | sed 's/^$dir_root//g')

path_dir给出后缀路径(比如命令中可能等于/image/)。

最后:

sed -i "$nb_ligne s/\".*\"/\"http\:\/\/mywebsite.com$path_dir\"/g" $i

但是最后一条命令不起作用 ($i is the current filename)。然而,

我已经使用双引号来扩展环境变量了。

撇开使用基于行的工具编辑 HTML 是否是个好主意的问题,假设您可以保证 HTML 文件的格式永远不会改变:

gawk -i inplace -v dir="$path_dir" '/<base/ { sub(/".*"/, "\"http://mywebsite.com" dir "\""); } 1' "$i"

为这个任务使用 sed 不是一个好主意,因为你最终将变量替换到 sed 代码中,这意味着它将被视为代码,然后你 运行 进入通常的代码注入问题。例如,如果你的路径包含 &,你会得到奇怪的结果,因为 & 在使用它的上下文中对 sed 有特殊意义,这是最不可怕的事情之一如果其他人控制路径名(GNU sed 可以使用 s///e 执行任意命令,这会很有趣)。

使用 awk 而是从一开始就将 $path_dir 视为数据来回避问题。 awk代码本身是

/<base/ {  # in lines that contain "<base"
           # substitute this regex with this string. The regex and string
           # are taken from your sed command.
  sub(/".*"/, "\"http://mywebsite.com" dir "\"")
}
1          # afterwards, print all lines. (1 means true here, and printing
           # is the default action)

如果你想要 s///g 的效果,请使用 gsub 而不是 sub,但你想要替换某物的所有实例对我来说没有意义包含在 "" 中以防匹配行中有多个。老实说,它看起来很脆弱。您可能需要考虑更严格的正则表达式,例如

sub(/href=".*"/, "href=\"http://mywebsite.com" dir "\"");

至少。甚至 /<base href=".*"/.

nb_ligne 不是这个任务所必需的,所以我把它省略了。

我使用的唯一特定于 GNU 的功能是 -i inplace 用于就地编辑,因此如果您有 mawk 或非常旧的 gawk,请将其保留并使用类似

cp "$i" "$i"~ && awk -v dir="$path_dir" '/<base/ { sub(/".*"/, "\"http://mywebsite.com" dir "\""); } 1' "$i"~ > "$i"

坚持使用 sed,这是一个单一的替换,它可以完成您似乎正在做的事情。

sed -i "s%\(<base href=\)\"\"%\"http://mywebsite.com${i#$dir_root}\"%" "$i"

我删除了 /g 标记,因为您不可能在一份文档中有多个 <base> 标记,更不用说在同一行上有多个标记了。