I have 2 variables.
I need to get the difference between them.
$var (a,b,c,d) and $var2 (a,b,c,d,e,f,g).
How can i do this using xpath funtions?
Any help is appreciated.
Thanks in Advance
If tibco supports XPath 2.0, you can try using except and union (|) like so :
$var except $var2 | $var2 except $var
With XPATH 1.0 you can also use the union off diffs of nodesets (O'Reilly's XSLT Cookbook)
Elements from var not in var1: $var[count(. | $var1) != count($var1)]
Elements from var1 not in var: $var1[count(. | $var) != count($var)]
Therefor try:
$var[count(. | $var1) != count($var1)] | $var1[count(. | $var) != count($var)]
Related
Consider the situation:
echo '"field:bla"\n"field:"' \
| jq ' . | gsub( "^field:(?<val>.+?)?$" ; "(?(val)value=\(.val)|NULL)" )'
We're taking in a list of strings of the form: field:<VALUE>, where <VALUE> can be either '' (empty), or one or more characters.
The objective is to return: NULL if <VALUE> is '' (empty), or value=<VALUE> if non-empty.
the question is, can jq do this using conditional substitution ? based on whether or not the group <val> is set ? if so what is the syntax ? or is this not supported ?
PS: it's not a problem of whether this can be done, or how to do it, i'm just wondering if jq's gsub supports conditional group substitution, and if so what's the right syntax for it.
The closest you can get to a "conditional substitution" within sub would be along these lines:
(echo "field:bla"; echo "field:") |
jq -rR 'sub( "^field:(?<val>.*)$" ; "value=\(.val | if length==0 then "NULL" else . end )" ) '
This produces:
value=bla
value=NULL
You might also like to consider this alternative, which produces the same result:
(echo "field:bla"; echo "field:") |
jq -rR 'sub( "^(field:(?<val>.+)|field:)$" ; "value=\(.val // "NULL")" )'
In both these cases, replacing sub by gsub has no effect on the results.
I have two yaml files I am using. The first yaml file looks like this:
spring.yml
spring:
cloud:
gateway:
routes:
- id: someid
uri: someUri
predicates:
- Path=/somePath
filters:
- RewritePath=/someOtherPath
I have another file that just contains routes and looks like this:
routes.yml
routes:
- id: someid
uri: someOtherUri
predicates:
- Path=/somePath
filters:
- RewritePath=/someNewPath
My goal is to update the route in the first file with the value of the route in the second file. Note that the first file in reality will have many routes but for demonstration purposes I am only showing the first in this example. I have the following script which loops through to update the routes as necessary when the id's match:
#!/bin/sh
OVERRIDE_ROUTE_IDS=$(yq eval '.routes.[].id' routes.yml)
GENERATED_ROUTE_IDS=$(yq eval '.spring.cloud.gateway.routes.[].id' spring.yml)
SAVEIFS=$IFS # Save current IFS (Internal Field Separator)
IFS=$'\n' # Change IFS to newline char
OVERRIDE_ROUTE_IDS=($OVERRIDE_ROUTE_IDS) # split the `OVERRIDE_ROUTE_IDS` string into an array by the same name
GENERATED_ROUTE_IDS=($GENERATED_ROUTE_IDS) # split the `GENERATED_ROUTE_IDS` string into an array by the same name
IFS=$SAVEIFS # Restore original IFS
for (( i=0; i<${#OVERRIDE_ROUTE_IDS[#]}; i++ ))
do
if [[ "${GENERATED_ROUTE_IDS[*]}" =~ "${OVERRIDE_ROUTE_IDS[$i]}" ]]
then
echo "route ID ${OVERRIDE_ROUTE_IDS[$i]} exists in generated routes"
for (( j=0; j<${#GENERATED_ROUTE_IDS[#]}; j++ ))
do
if [[ "${GENERATED_ROUTE_IDS[$j]}" == "${OVERRIDE_ROUTE_IDS[$i]}" ]]
then
echo "index of route ${GENERATED_ROUTE_IDS[$j]} is $j"
echo "$i"
ROUTE_TO_USE=$(yq eval ".routes.[$i]" routes.yml)
$(yq ".spring.cloud.gateway.routes.[$j] = $ROUTE_TO_USE" spring.yml)
fi
done
else
echo "no match so add to top of routes"
fi
done
My assumption is this command should update spring.yml file with the new route in place of the one that was identified with the same id:
$(yq ".spring.cloud.gateway.routes.[$j] = $ROUTE_TO_USE" application.yml)
But I am getting the following error
Error: Parsing expression: Lexer error: could not match text starting at 1:37 failing at 1:39 unmatched text: "id"
I'm stumped on this and not sure what I'm doing wrong at this point. For reference I am using yq version 4.17.2.
Be aware that yq does not emit a data structure, it emits a string. $ROUTE_TO_USE would be, for example,
id: someid
uri: someOtherUri
predicates:
- Path=/somePath
filters:
- RewritePath=/someNewPath
This is YAML source. Pasting this into the following yq command leads to invalid syntax; yq's expression syntax is not literal YAML. This is what the error tries to tell you.
What you want to do is to process both inputs in a single yq command:
yq ea "select(fi==0).spring.cloud.gateway.routes.[$j] = "`
`"select(fi==1).routes.[$i] | select(fi==0)" spring.yml routes.yml
ea is shorthand for eval-all which you need for processing multiple input files at the same time. fi is a shorthand for fileIndex, which is used to select the appropriate file. Piping the result to | select(fi==0) ensures that only the first (modified) file is written out. I split the long string into multiple lines using backticks for readability.
I ended up getting a solution from the creator of yq. The solution below is what I used:
yq ea '
(select(fi==0) | .spring.cloud.gateway.routes.[].id) as $currentIds |
(select(fi==1) | [.routes.[] | select( [$currentIds != .id] | all )] ) as $newRoutes |
( $newRoutes + .spring.cloud.gateway.routes + .routes) as $routesToMerge |
(
(($routesToMerge | .[] | {.id: .}) as $item ireduce ({}; . * $item )) as $uniqueMap
| ( $uniqueMap | to_entries | .[]) as $item ireduce([]; . + $item.value)
) as $mergedArray
| select(fi == 0) | .spring.cloud.gateway.routes = $mergedArray
' spring.yml routes.yml
This matches on id. If there is a match it uses the value of what's in routes.yml. If there is no match it add it the top top of the routes.
I have a string that is sometimes
xxx.11_222_33_44_555.yyy
and sometimes
xxx.11_222_33_44.yyy
I would like to:
Check if has 4 occourances of _ (figured out how to do it).
If so - remove string's _33 (the 33 string changes, can be any number), so I am left with xxx.11_222_44.yyy.
Using sed :
sed 's/\(_[0-9]*\)_[0-9]*\(_[0-9]*_[0-9]*\)/\1\2/'
It matches the four underscores and replace the whole by the needed parts.
Test run :
$ echo "xxx.11_222_33_44_555.yyy" | sed 's/\(_[0-9]*\)_[0-9]*\(_[0-9]*_[0-9]*\)/\1\2/'
xxx.11_222_44_555.yyy
$ echo "xxx.11_222_33_44.yyy" | sed 's/\(_[0-9]*\)_[0-9]*\(_[0-9]*_[0-9]*\)/\1\2/'
xxx.11_222_33_44.yyy
perhaps something like this
echo "xxx.11_222_33_44.yyy" | sed -e's/\.\([0-9]\+\)_\([0-9]\+\)_\([0-9]\+\)_\([0-9]\+\)\./.\1_\2_\4./'
which checks if there are 4 groups of numbers separated by _ between the two dots and if yes, it leaves out the third group
try this;
echo "xxx.11_222_33_44_555.yyy" | awk -F'_' 'NF>4{print $1"_"$2"_"$4"_"$5};'
Solution using perl and Lookahead and Lookbehind
$ a="xxx.11_222_33_44_555.yyy"
$ perl -pe 's/\.\d+_\d+_\K\d+_(?=\d+_\d+\.)//' <<< "$a"
xxx.11_222_44_555.yyy
I am trying to modify a if loop in a large code base.My need is as follows.
The code may contain as follows.This is just a random combination example of an if condition.I need to modify if else if conditions alone.
if((A==B)&&(C==D)&&((E==F)||(G==H))||(I)&&(J!=K))
should be modified as
if((string.Compare(A,B)==0)&&(string.Compare(C,D)==0)&&((string.Compare(E,F)==0)||(string.Compare(G,H)==0))||(I)&&(string.Compare(J,K)!=0))
I tried with Java but utterly failed.I believe this is possible with sed or awk.Any help?
You could do it basically with any language that supports regular expressions replacement.
Here's a 3 lines working C# example:
string text = "if((A==B)&&(C==D)&&((E==F)||(G==H))||(I)&&(J!=K))";
string pattern = #"\((?:(\w)((?:=|!)=)(\w))\)";
var replaced = Regex.Replace(text, pattern, m => string.Format("(string.Compare({0},{1}){2}0)", m.Groups[1].Value, m.Groups[3].Value, m.Groups[2].Value));
Console.WriteLine(replaced);
And the result:
if((string.Compare(A,B)==0)&&(string.Compare(C,D)==0)&&((string.Compare(E,F)==0)||(string.Compare(G,H)==0))||(I)&&(string.Compare(J,K)!=0))
sed -r 's/([[:alpha:]])([=!]=)([[:alpha:]])/string.Compare(\1,\3) \2 0/g'
The spaces around \2 aren't strictly necessary.
Basically you don't need to use awk or sed to construct the if statement. It is enough to write:
if [ A == B -a C == D -a ( E == F -o G == H ) -o I -a J != K ]; then
your code here
fi
The == operator compares strings. If you wanted to compare numbers use -eq operator. See this chapter of bash manual (scroll down to test command), and the description of usage of primary conditional expressions
I'm trying to write a bash script that increments the version number which is given in
{major}.{minor}.{revision}
For example.
1.2.13
Is there a good way to easily extract those 3 numbers using something like sed or awk such that I could increment the {revision} number and output the full version number string.
$ v=1.2.13
$ echo "${v%.*}.$((${v##*.}+1))"
1.2.14
$ v=11.1.2.3.0
$ echo "${v%.*}.$((${v##*.}+1))"
11.1.2.3.1
Here is how it works:
The string is split in two parts.
the first one contains everything but the last dot and next characters: ${v%.*}
the second one contains everything but all characters up to the last dot: ${v##*.}
The first part is printed as is, followed by a plain dot and the last part incremented using shell arithmetic expansion: $((x+1))
Pure Bash using an array:
version='1.2.33'
a=( ${version//./ } ) # replace points, split into array
((a[2]++)) # increment revision (or other part)
version="${a[0]}.${a[1]}.${a[2]}" # compose new version
I prefer "cut" command for this kind of things
major=`echo $version | cut -d. -f1`
minor=`echo $version | cut -d. -f2`
revision=`echo $version | cut -d. -f3`
revision=`expr $revision + 1`
echo "$major.$minor.$revision"
I know this is not the shortest way, but for me it's simplest to understand and to read...
Yet another shell way (showing there's always more than one way to bugger around with this stuff...):
$ echo 1.2.3 | ( IFS=".$IFS" ; read a b c && echo $a.$b.$((c + 1)) )
1.2.4
So, we can do:
$ x=1.2.3
$ y=`echo $x | ( IFS=".$IFS" ; read a b c && echo $a.$b.$((c + 1)) )`
$ echo $y
1.2.4
Awk makes it quite simple:
echo "1.2.14" | awk -F \. {'print $1,$2, $3'} will print out 1 2 14.
flag -F specifies separator.
If you wish to save one of the values:
firstVariable=$(echo "1.2.14" | awk -F \. {'print $1'})
I use the shell's own word splitting; something like
oIFS="$IFS"
IFS=.
set -- $version
IFS="$oIFS"
although you need to be careful with version numbers in general due to alphabetic or date suffixes and other annoyingly inconsistent bits. After this, the positional parameters will be set to the components of $version:
$1 = 1
$2 = 2
$3 = 13
($IFS is a set of single characters, not a string, so this won't work with a multicharacter field separator, although you can use IFS=.- to split on either . or -.)
Inspired by the answer of jlliagre I made my own version which supports version numbers just having a major version given. jlliagre's version will make 1 -> 1.2 instead of 2.
This one is appropriate to both styles of version numbers:
function increment_version()
local VERSION="$1"
local INCREMENTED_VERSION=
if [[ "$VERSION" =~ .*\..* ]]; then
INCREMENTED_VERSION="${VERSION%.*}.$((${VERSION##*.}+1))"
else
INCREMENTED_VERSION="$((${VERSION##*.}+1))"
fi
echo "$INCREMENTED_VERSION"
}
This will produce the following outputs:
increment_version 1 -> 2
increment_version 1.2 -> 1.3
increment_version 1.2.9 -> 1.2.10
increment_version 1.2.9.101 -> 1.2.9.102
Small variation on fgm's solution using the builtin read command to split the string into an array. Note that the scope of the IFS variable is limited to the read command (so no need to store & restore the current IFS variable).
version='1.2.33'
IFS='.' read -r -a a <<<"$version"
((a[2]++))
printf '%s\n' "${a[#]}" | nl
version="${a[0]}.${a[1]}.${a[2]}"
echo "$version"
See: How do I split a string on a delimiter in Bash?
I'm surprised no one suggested grep yet.
Here's how to get the full version (not limited to the length of x.y.z...) from a file name:
filename="openshift-install-linux-4.12.0-ec.3.tar.gz"
find -name "$filename" | grep -Eo '([0-9]+)(\.?[0-9]+)*' | head -1
# 4.12.0