0

我正在开发一个函数来执行多个key=value替换sed

batch_sub () {
    local file="${@: -1}" # Last argument is file to be changed.
    [[ -w "${file}" ]] || { echo "Invalid file: ${file}" ; return 1 ; }
    for arg in "${@}" ; do
        [[ $arg =~ (.*)=(.*) ]] || continue
        # 'trim_str' will trim leading & trailing spaces.
        local key=$(trim_str "${BASH_REMATCH[1]}")
        local value=${BASH_REMATCH[2]}
        sed -i 's@'"\(${key}"' *=\).*@\1'"${value}"'@' "${file}"
    done
}

这个函数接受如下参数:

batch_sub "a = x" "b = y" "c = z" "a.b.d.e=udp://b:8080" "/tmp/file"

并且工作正常。但我想要的是让它接受这样的论点:

batch_sub "a = x b = y c = z a.b.d.e=udp://b:8080" "/tmp/file"

此外,如果sed在这个函数中只调用一次就好了:

sed -i -e 's/$key=/\1$value/'
       -e 's/$key1=/\1$value1/'
       '/tmp/file'

请指教。

4

2 回答 2

3

如果 sed 在这个函数中只被调用一次就好了

您可以一次调用 sed 进行多次替换。例如

sed -i 's/a/b/;s/p/q/'

因此,从所有参数中,生成一个包含所有替换的字符串,然后一劳永逸地调用 sed。

但我想要的是让它接受像这样的参数
batch_sub "a = xb = yc = z abde=udp://b:8080" "/tmp/file"

为什么?
“a = xb = yc = z abde=udp://b:8080” 在我看来是模棱两可的。
至少尝试为对设置某种分隔符,例如
"a = x; b = y; c = z; abde=udp://b:8080"

于 2012-10-16T10:58:51.080 回答
1

这可能很疯狂。但是使用您设置的要求可能是这样的:)

#!/bin/bash

declare -r self=${0##*\/}

# Left and right trim
trim_str()
{
    local tmp="${1##[[:space:]]}"
    printf "%s" "${tmp%%[[:space:]]}"
}

# Replace \ with \\, / with \/, & with \&
sed_esc_repl()
{
    local tmp="${1//\\/\\\\}"
    tmp="${tmp//\//\\/}"
    tmp="${tmp//&/\&}"
    printf "%s" "$tmp"
}

# Additional escape on search pattern
# escape *.][|
sed_esc_key()
{
    local tmp=$(sed_esc_repl "$1")
    tmp="${tmp//./\.}"
    tmp="${tmp//\*/\*}"
    tmp="${tmp//|/\|}"
    tmp="${tmp//[/\[}"
    tmp="${tmp//]/\]}"
    printf "%s" "$tmp"
}

batch_sub()
{
    local file=""
    local -a argv
    local key=
    local val=
    local sedstr=""
    local old_ifs=$IFS

    if (( $# < 2 )); then
        printf "Usage: $self \"replacement pairs\" <file>\n"
        return 1
    elif (( $# > 2 )); then
        file="${@: -1}"
        argv=( "${@:1:$(($#-1))}" ) # Everything but last arg
    else
        file="$2"
        argv=( "$1" )
    fi

    [[ -w "${file}" ]] || { printf "Invalid file: %s\n" "${file}"; return 1; }

    # Set IFS  to match space and equal sign.
    IFS='='' '
    for arg in ${argv[@]}; do
        if [[ "$key" != "" ]]; then
            sedstr+="s/\("$(sed_esc_key "$key")"\) *=.*/\1="$(sed_esc_repl "$arg")"/g;"
            key=
        else
            key="$arg"
        fi
    done
    IFS="$old_ifs"

    printf "sed string:\"%s\"\n\n" "${sedstr%%;}"

    sed -i "${sedstr%%;}" "$file"
}

# Create example / test file:
printf "\
x[a-zA-Z].*? = mixture1
x[a-zA-Z].*p? = mixture2
x = foo
b*x = fafa
a\\\\1c = moo
a.b$.d.e=zim zala bim
" > tst

batch_sub "$@"

exit $?

通过即运行:

./keysw "x[a-zA-Z].*p? = xb*x = ya\1c = \z\n\1 ab$.de=udp&://b:\8080" tst && cat tst

或者

./keysw "x[a-zA-Z].*p? = x" \
        "b*x = y" \
        "a\1c = \z\n\1" \
        "a.b$.d.e=udp&://b:\8080" \
        tst && cat tst

给予;

文件:

x[a-zA-Z].*? = mixture1
x[a-zA-Z].*p? = mixture2
x = foo
b*x = fafa
a\1c = moo
a.b$.d.e=zim zala bim

结果:

x[a-zA-Z].*? = mixture1
x[a-zA-Z].*p?=x
x = foo
b*x=y
a\1c=\z\n\1
a.b$.d.e=udp&://b:\8080
于 2012-10-16T12:49:09.880 回答