Reputation: 3847
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:
svn propget --strict PROPNAME TARGET | sed -e 's/OLD/NEW/g' | svn propset PROPNAME --file=- TARGET
svn propset PROPNAME --file=<(svn propget --strict PROPNAME TARGET | sed -e 's/OLD/NEW/g') TARGET
SVN_EDITOR="sed -i -e 's/OLD/NEW/g'" svn propedit PROPNAME TARGET
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
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