Bash - 如何用一个在变量中定义多行的模式开始替换单行?

时间:2016-01-13 01:56:34

标签: bash replace sed grep heredoc

我有一个文件,在某些时候包含如下所示的行:

VIRTUAL_ENV="/afs/sern.ch/user/l/lronhubbard/virtual_environment"

它是文件中唯一以VIRTUAL_ENV开头的行。使用脚本,我想用以下行替换这一行:

directory_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
directory_env="$(dirname "${directory_bin}")"
VIRTUAL_ENV="${directory_env}"

怎么可以这样做?

首先,我在脚本中的here文档中存储了替换行:

IFS= read -d '' text << "EOF"
directory_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
directory_env="$(dirname "${directory_bin}")"
VIRTUAL_ENV="${directory_env}"
EOF

接下来,我可以使用xxx替换文件中的行(此处为sed):

sed -i "s/^VIRTUAL_ENV.*/xxx/g" test.txt

现在,我如何在${text}命令中使用已定义的文档变量xxx及其所有换行符而不是sed

编辑:根据rslemos的建议,我使用临时文件而不是here文档实现了以下内容:

#!/bin/bash

temporary_filename="$(tempfile)"

cat > "${temporary_filename}" << "EOF"
directory_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
directory_env="$(dirname "${directory_bin}")"
VIRTUAL_ENV="${directory_env}"
EOF

sed -i "/^VIRTUAL_ENV.*/ {
   r ${temporary_filename}
   d
}" test.txt

rm "${temporary_filename}"

我仍然想知道如何直接使用这里的文档,这样我就不会不必要地使用硬盘了。

3 个答案:

答案 0 :(得分:2)

我会单独使用sed

/^VIRTUAL_ENV/{
r source.txt
d
}

其中source.txt是替换以&#34; VIRTUAL_ENV&#34;开头的新行的文件。

如果你真的需要新行(source.txt)作为here文档,/dev/fd/0技巧将起作用(至少在Linux中)。但您也可以合并mktempmkfifo

修改

要解决&#34; 我仍然想知道如何直接使用此处的文档&#34;:

!/bin/bash

sed -e '/^VIRTUAL_ENV/{' -e 'r /dev/fd/0' -ed -e'}' test.txt << "EOF"
directory_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
directory_env="$(dirname "${directory_bin}")"
VIRTUAL_ENV="${directory_env}"
EOF

不是很便携,因为/dev/fd/0部分(在Linux中有效)。

答案 1 :(得分:1)

这可能适合你(GNU sed&amp; bash):

cat <<\! | sed $'/PATTERN/{r /dev/stdin\n;d}' file
HERE DOCUMENT
STUFF
HERE
!

使用catstdin传递给sed命令,并使用r命令将其作为文件读取。

N.B。 \!引用此处文档中的所有文字,使用!来插入变量。 $'...'允许需要换行符的命令(即raicR,{{1}等sed命令},w)写成一行。

答案 2 :(得分:0)

sed的许多方言都允许您在替换中转义换行符:

sed -i 's/^VIRTUAL_ENV.*/directory_bin="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\
directory_env="$(dirname "${directory_bin}")"\
VIRTUAL_ENV="${directory_env}"/' test.txt

这是一个跨越三行的单引号字符串,并在每个内部行边界处添加了反斜杠以继续。

这对于每个sed变体都不可移植,但至少可以在* BSD(包括OSX)和Linux上正常工作。

顺便说一下,您也可以简单地将所有这些命令放在一行上,并在它们之间用分号。