插入多行并使用sed保持缩进

时间:2016-05-16 03:47:11

标签: bash sed insert

我想要在文件中插入一些Tython函数。插入多行本身可以很好地使用变量和一些\n,但不保留缩进。因为它是Python代码,这是一个大问题,代码无法正常工作。

以下是我的尝试:

cat sed-insertlines.sh

#!/bin/bash

read -r -d '' lines_to_insert << 'EOF'
def string_cleanup(x, notwanted):\n
    for item in notwanted:\n
        x = re.sub(item, '', x)\n
    return x\n
EOF

lines_to_insert=$(echo ${lines_to_insert} )

sed  -i "/import re  # Regular Expression library/a $lines_to_insert" sed-insertlines.txt

但这是我cat sed-insertlines.txt时最终得到的结果:

#!/bin/python

import re  # Regular Expression library
def string_cleanup(x, notwanted):
 for item in notwanted:
 x = re.sub(item, '', x)
 return x


def string_replace(i_string, pattern, newpattern):
    string_corrected = re.sub(pattern, newpattern, i_string)
    return string_corrected

线条在那里,但缩进消失了。

3 个答案:

答案 0 :(得分:7)

首先,让我们将数据干净地放入shell变量中。这是一种方式:

lines_to_insert=$(cat<<'EOF'
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x
EOF
)

请注意,没有添加\n;你可以使用你想要未经修改的文本,但唯一的限制是它不能包含一个由EOF组成的行(如果有,你可以更改here-doc分隔符。)不幸的是,稍后使用sed将通过解释一些反斜杠序列来修改文本。

sed a命令的正确语法如下:

sed -i '/^import re/a \
def string_cleanup(x, notwanted):\
    for item in notwanted:\
        x = re.sub(item, '', x)\
    return x
'

(常见的sed 'a line to insert'不是Posix标准,并且不允许在行上放置前导空格。正确的语法如上所示; a后跟空格,后面跟着通过延续标记和换行符。)

请注意,除了最后一行之外的每一行都有一个连续标记(一个尾部反斜杠)。我们可以在上面的文本中加入这些内容,但这会使您无法准确使用要插入的文本。

相反,当我们将shell变量插入sed命令时,我们将使用全局搜索和替换语法插入反斜杠:

# The following works with bash 4.3 and up
sed -i.bak "/^import re/a \
${lines_to_insert//$'\n'/$'\\\n'}
" sed-insertlines.txt

# Prior to v4.3, quoting worked differently in replacement
# patterns, and there was a bug with `$'...'` quoting. The
# following will work with all bashes I tested (starting with v3.2):
nl=$'\n' bsnl=$'\\\n'
sed -i.bak "/^import re/a \
${lines_to_insert//$nl/$bsnl}
" sed-insertlines.txt

另一个解决方案是使用mapfile命令将行读入数组:

mapfile -t lines_to_insert <<'EOF'
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x
EOF

现在我们可以使用printf添加反斜杠:

sed -i.bak "/^import re/a \
$(printf '%s\\\n' "${lines_to_insert[@]}")
" sed-insertlines.txt

(搜索和替换语法也适用于数组,但我认为printf命令更具可读性。)

不幸的是,在文本之后添加了一个额外的换行符,因为原始文本中的所有行都是继续的。如果这是不希望的,可以通过在printf的开头而不是结尾处插入反斜杠和换行符,在第二个解决方案中轻松删除它,使得命令稍微不易读取:

sed -i.bak "/^import re/a $(printf '\\\n%s' "${lines_to_insert[@]}")
" sed-insertlines.txt

最后,基于Benjamin W的一个很好的答案,这里有一个使用sed r命令和进程替换的版本(以避免临时文件):

sed '/^import re/r '<(cat<<'EOF'
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x
EOF
) sed-insertlines.txt

答案 1 :(得分:3)

我会使用sed r命令,它在当前周期之后插入文件的内容:

#!/bin/bash

# Write code to be inserted into 'insertfile' with proper indentation
cat <<'EOF' > insertfile
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x
EOF

# Sed with r command
sed -i '/import re  # Regular Expression library/r insertfile' sed-insertlines.txt

# Remove temp file
rm -f insertfile

导致

import re  # Regular Expression library
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x


def string_replace(i_string, pattern, newpattern):
    string_corrected = re.sub(pattern, newpattern, i_string)
    return string_corrected

答案 2 :(得分:1)

如果您有兴趣,可以使用Awk解决方案:

<强> python_file:

#!/bin/python

import re  # Regular Expression library

def string_replace(i_string, pattern, newpattern):
    string_corrected = re.sub(pattern, newpattern, i_string)
    return string_corrected

我们的剧本

#!/bin/bash
read  -rd '' lines_to_insert << 'EOF'
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x
EOF
awk -v from_shell="$lines_to_insert" '
{
if ($0 ~ /import re  # Regular Expression library/){
printf "%s\n%s\n",$0,from_shell
}
else{
print $0
}
}' python_file

<强>输出:

#!/bin/python

import re  # Regular Expression library
def string_cleanup(x, notwanted):
    for item in notwanted:
        x = re.sub(item, '', x)
    return x

def string_replace(i_string, pattern, newpattern):
    string_corrected = re.sub(pattern, newpattern, i_string)
    return string_corrected

注意:

我已从\n删除了$lines_to_insert