mikefarah / yq

yq is a portable command-line YAML, JSON, XML, CSV, TOML and properties processor
https://mikefarah.gitbook.io/yq/
MIT License
11.29k stars 565 forks source link

startswith, endswith throwing "invalid input text" error when trying to use with select #2026

Open nicktimko opened 2 months ago

nicktimko commented 2 months ago

Describe the bug startswith and related functions don't work as in jq

Version of yq: 4.43.1 Operating system: macOS 14.4.1 Installed via: Homebrew

Script

#!/bin/bash

input='{"ppl":[{"name":"alice"},{"name":"bob"},{"name":"charlie"},{"name":"deb"}]}'
tmp_names=/tmp/names
yq -P <<<$input >$tmp_names.yaml
jq <<<$input >$tmp_names.json

python3 -c "import platform;print(platform.platform())"
jq --version
yq --version

echo Only Bobs:  # these all work as expected
jq '.ppl[] | select(.name == "bob")' $tmp_names.json
yq '.ppl[] | select(.name == "bob")' $tmp_names.json
yq '.ppl[] | select(.name == "bob")' $tmp_names.yaml

echo Other names ending in B:
jq '.ppl[] | select(.name | endswith("b"))' $tmp_names.json  # returns bob/deb
yq '.ppl[] | select(.name | endswith("b"))' $tmp_names.json  # Error: 1:25: invalid input text "endswith(\"b\"))"
yq '.ppl[] | select(.name | endswith("b"))' $tmp_names.yaml  # Error: 1:25: invalid input text "endswith(\"b\"))"

Output

macOS-14.4.1-arm64-arm-64bit
jq-1.7.1
yq (https://github.com/mikefarah/yq/) version v4.43.1
Only Bobs:
{
  "name": "bob"
}
{
  "name": "bob"
}
name: bob
Other names ending in B:
{
  "name": "bob"
}
{
  "name": "deb"
}
Error: 1:25: invalid input text "endswith(\"b\"))"
Error: 1:25: invalid input text "endswith(\"b\"))"

Additional context Add any other context about the problem here.

mikefarah commented 1 month ago

Thanks for raising; note that my yq is not a wrapper around jq; but rather tries to re-implement it in go (so you don't need jq or python to run it).

The reason you're getting that error is because I haven't yet added that operator in :)

You can still get what you want by either using the match operator:

echo '{"ppl":[{"name":"alice"},{"name":"bob"},{"name":"charlie"},{"name":"deb"}]}'  | yq '.ppl[] | select(.name | match(".*b"))'

Or wildcards:

echo '{"ppl":[{"name":"alice"},{"name":"bob"},{"name":"charlie"},{"name":"deb"}]}'  | yq '.ppl[] | select(.name == "*b")'
mikefarah commented 1 month ago

Also, happy to take PRs for adding startswith / endswith :)

nicktimko commented 1 month ago

Ah, I guess I'd put something in the lexer/parser or whatever that if it sees something that looks like a function, but isn't recognized, an error that it's an unknown function or not defined rather than "invalid input text"

$ echo {} | jq ".thing | select(.whatever)"  # runs fine (no output)
$ echo {} | jq ".thing | asdf(.whatever)"
jq: error: asdf/1 is not defined at <top-level>, line 1:
.thing | asdf(.whatever)
jq: 1 compile error
balki commented 1 week ago

I think the wildcard comparison already handles startswith and endswith. I don't see any benefit in adding those other than matching jq API

❯ yq '.ppl[] | select(.name == "*b")' $tmp_names.yaml
name: bob
name: deb

❯ yq '.ppl[] | select(.name == "b*")' $tmp_names.yaml
name: bob

Using regex instead of simple wildcard

❯ yq '.ppl[] | select(.name | test("b.*"))' $tmp_names.yaml
name: bob
name: deb