musiphil
musiphil

Reputation: 3847

Editing a svn property through a filter command

I'd like to apply a filter (e.g. sed -e 's/OLD/NEW/g') to svn property values, recursively.

First, on a single target, I could do one of the following:

  1. svn propget --strict PROPNAME TARGET | sed -e 's/OLD/NEW/g' | svn propset PROPNAME --file=- TARGET
  2. svn propset PROPNAME --file=<(svn propget --strict PROPNAME TARGET | sed -e 's/OLD/NEW/g') TARGET
  3. SVN_EDITOR="sed -i -e 's/OLD/NEW/g'" svn propedit PROPNAME TARGET
  4. svn propedit --editor-cmd "sed -i -e 's/OLD/NEW/g'" PROPNAME TARGET

Which one would be the best?

What I don't like about 1 or 2 is that even if the svn propget ... | sed ... pipeline fails, svn propset is still executed, probably on an empty stdin, resetting the property.

On the other hand, what I don't like about 3 or 4 is that the whole filtering command line should be quoted and made into one string, which sometimes makes quoting and parameter expansion tricky. And note the option -i — the filtering command should take a file parameter and edit it in place, which means these methods cannot be applied to commands that don't offer such in-place editing feature, such as tr or even awk.

Is there any better alternative, or any possible improvements to one of these?

Second, how do I do this recursively — i.e. on every target under the given TARGET that has the specified property PROPNAME? I cannot just add -R to svn propget as it will concatenate everything into one stream of text, and svn propedit doesn't even have -R. It seems that I have to do something like:

svn proplist -R --only-with PROPNAME TARGET | while read target
do
    # do one of the above on "$target"
done

but svn proplist doesn't have such a thing called --only-with, and I cannot easily parse its output and grab the names of only those targets that have the property.

Is there any good way to do it?

Upvotes: 1

Views: 566

Answers (1)

musiphil
musiphil

Reputation: 3847

The output of svn propget -R is not easily parsable, but that of svn propget -v -R is, so I decided to run it once and parse its output.

First, I tried process substitution >(...), but as asked in this thread I couldn't enforce or wait for the termination of such subprocesses, so the output message was a mess, though it did work. So, as answered in the same thread, I used coprocesses:

#!/bin/bash
set -o errexit -o pipefail

if [ $# -le 2 ]; then
    echo "usage: $0 PROPNAME TARGET COMMAND [ARGS...]" >&2
    exit 1
fi
PROPNAME=$1; shift
TARGET=$1; shift
COMMAND=$1; shift

cleanup_and_wait() {
    if [[ ${COPROC[1]} =~ ^[0-9]+$ ]]; then
        eval "exec ${COPROC[1]}<&-"
        wait $COPROC_PID
    fi
}

unset curr_target
unset prev_line
while IFS= read -r line; do
    case "$line" in
    "    "*)
        line=${line:4}
        if declare -p prev_line >/dev/null 2>&1; then
            echo "$prev_line" >&${COPROC[1]}
        fi
        prev_line=$line
        ;;
    "  "*)
        line=${line:2}
        if [ "$line" != "$PROPNAME" ]; then
            echo "Unexpected property: $line" >&2
        fi
        cleanup_and_wait
        coproc { "$COMMAND" "$@" | svn propset "$PROPNAME" --file=- "$curr_target"; } >&2
        unset prev_line
        ;;
    "Properties on '"*"':")
        line=${line%"':"}
        line=${line#"Properties on '"}
        curr_target=$line
        ;;
    esac
done < <(svn propget --verbose --recursive "$PROPNAME" "$TARGET")
cleanup_and_wait

One minor thing I don't like is that even when the given filter does not change the property value, svn propset is still executed and prints property 'PROPNAME' set on 'TARGET'; it would be nice to see the messages only for those targets whose property values actually got changed.

Upvotes: 1

Related Questions