这个问题是关于使用 渲染模板sed
。
我有模板文件nginx_app
:
server {
root ${drt}/${domain}/;
server_name ${domain} www.${domain};
}
我在另一个脚本中使用该文件:
#!/bin/bash
domain="$1" && test -z ${domain} && return
sed "s/\${domain}/${1}/g" "nginx_app" > "/etc/nginx/sites-available/${domain}.conf"
当然,目的是有一个example.com.conf
作为渲染模板,基于。nginx_app
考虑上面的代码:
sed "s/\${domain}/${1}/g" "nginx_app" > "/etc/nginx/sites-available/${domain}.conf"
据我了解,这段代码执行以下操作:
${domain}
它根据${1}
( ) 的脚本参数更改(尚未展开的)字符串example.com
,这是在内存,但不更改nginx_app
文件本身。- 它
nginx_app
通过将渲染版本重定向到/etc/nginx/sites-available/${domain}.conf
.
假设我在这里是准确的,我必须说,从代码来看,这些操作对我来说非常不直观。我对该代码的描述准确吗?
如果我的描述不够准确,请更正。
答案1
是的,您对文本第二部分的理解是正确的。
sed
与许多其他 Unix 工具一样,可以说它的作用就像筛选。过滤器接受输入、修改它并产生输出。在许多情况下,执行过滤的实用程序甚至可能不知道它正在读取或写入文件。事实上,它可以直接从另一个过滤器实用程序读取或写入。这对于实用程序本身是透明的,并且由 shell 负责设置。
忽略以下不必要使用的事实cat
,
cat <file | sed 's/^\([^=]*\)=\(.*\)$/#\1=\2 # old/' | tr -s ' ' >newfile
上面的实用程序都不知道从文件中读取或写入文件。 cat
从其读取标准输入流(shell 安排其中包含来自 的数据file
),可能一次一行,并将其输出写入其标准输出流(无需修改)。
sed
读取自它是标准输入流(shell 已将其连接到 的输出流cat
)对每行输入进行一些修改,并将结果写入其标准输出流(连接到tr
)。
tr
也只是写入标准输出,但 shell 以输出进入文件的方式设置重定向newfile
。
在此过程的每个步骤中,每个实用程序可能只保存通过管道的最低限度必需的数据(在 RAM 中分配的缓冲区中)。这意味着newfile
在文件中的数据file
被完全读取之前,结果可能会开始写入(在本示例中)。
你的具体例子:
sed "s/\${domain}/${1}/g" "nginx_app" > "/etc/nginx/sites-available/${domain}.conf"
发生了什么:
shell 解析命令行并查找变量和重定向。
文件
/etc/nginx/sites-available/${domain}.conf
($domain
扩展为该变量的值)如果存在则被截断(或清空),如果不存在则创建为空文件。该实用程序使用操作数(其中已扩展为其值,但尚未扩展,因为已使用 进行转义)和
sed
来调用。 shell 还会将标准输出连接到您要重定向到的文件。s/\${domain}/${1}/g
$1
${domain}
$
\
nginx_app
sed
由于
sed
有两个操作数,它会假设第二个操作数是要读取的文件。它打开它并逐行读取它,同时在第一个操作数中应用编辑命令。任何输出都会进入您重定向到的文件。
既然人们提到sed -i
:
-i
使sed
对sed
给定文件执行更改的标志到位,也就是说,它不会写入其标准输出,但其更改的结果将反映在用于输入的同一文件中。
在内部,sed
通过写入临时文件它稍后用它替换原始文件。
该-i
标志采用一个(对于 GNUsed
和该实用程序的一些其他实现是可选的,对于其他实用程序是强制的)参数:
sed -i .bak '...some sed script...' filename
这会导致使用给定字符串作为新文件名后缀来备份原始文件。
我通常会尝试阻止人们使用-i
with sed
,原因如下:
- 它是一个非标准扩展POSIX 标准
sed
。 - 它在不同的 Unice 上表现不同。查看问题如何使用 sed -i (就地编辑)实现可移植性?
- 在测试或开发脚本时使用它是危险的,因为如果脚本
sed
有问题,它可能会破坏数据。
相反,我建议人们做一些类似的事情
sed '...some sed script...' file >file-new && mv file-new file
&& mv
...并且仅在sed
零件经过正确测试后才添加零件。
意思&& mv
是“如果上一个命令成功终止(没有错误),则重命名该文件”。
答案2
如果没有该-i
标志,任何命令的结果sed
都将写入标准输出。
在本例中,您将标准输出重定向到另一个文件 ( /etc/nginx/sites-available/${domain}.conf
)。
答案3
听起来您好像在问为什么必须将输出重定向到文件中而不是sed
直接写入该文件。如果是这样,有几个原因。
- 实际上,您可以通过
-i
的参数来做到这一点sed
。 - 这不是默认行为,因为它不遵循设计与其他程序对话的程序的常见 UNIX 哲学(https://en.wikipedia.org/wiki/Unix_philosophy)。
许多 UNIX shell 实用程序背后的想法是将它们链接在一起 (foo | bar | baz
)。这允许您对数据执行复杂的处理,而无需在每个步骤之间将其缓冲到磁盘。