Is it possible to define a custom "#" escape functions in jq? - jq

We have a number of builtin format/escape functions: #csv, #sh, etc
… | #sh
#sh "\( … )"
It is possible to define a custom format/escape function, say #sql?

Um. Technically, yes. I don't think I could recommend it, but it does appear to be possible.
First of all, you need to understand that #csv, #sh etc aren't separate functions from jq's point of view. They're all implemented by the format/1 function. This function takes a single string argument that is the name of the format to use, e.g. "csv" or "sh". You can redefine this function, and lo and behold jq will use it for formatting!
jq -n 'def format(fstring): "XXX";[1,2,3]|#json'
"XXX"
Okay, that's not very useful. How about this?
jq -n '
def format(fstring):
if fstring=="sparkle" then
".:!" + (tostring) + "!:."
else
oldformat(fstring)
end
;
[1,2,3]|#sparkle
'
".:![1,2,3]!:."
It worked! But don't get too excited...
jq -n '
def format(fstring):
if fstring=="sparkle" then
".:!" + (tostring) + "!:."
else
oldformat(fstring)
end
;
[1,2,3]|#json
'
Uh oh, that just hangs. Although we were hoping to delegate to the original format when we don't know what to do with the format, we actually called our new format function recursively. It just keeps calling itself forever. It looks like we might be out of luck on our extremely cursed quest. However, if we read the jq manual carefully, there is a glimmer of hope:
Multiple definitions using the same function name are allowed. Each re-definition replaces the previous one for the same number of function arguments, but only for references from functions (or main program) subsequent to the re-definition. See also the section below on scoping.
Great! We can use this to save the old format function:
jq -n '
def oldformat(fstring):
format(fstring);
def format(fstring):
if fstring=="sparkle" then
".:!" + (tostring) + "!:."
else
oldformat(fstring)
end
;
[1,2,3]|(#sparkle,#json,#sh)
'
".:![1,2,3]!:."
"[1,2,3]"
"1 2 3"
I really don't recommend this: it's awkward to do, not terribly useful, and I have no idea if it's going to break something else. The format function doesn't appear to be documented so it's likely an unsupported implementation detail. But it was fascinating to find out that this is technically possible.

Related

zsh accepts syntactically incorrect loop construct

By mistake, I typed something like this:
for f in a b; echo a; echo x
The output produced was
a
a
x
as if I had written
for f in a b; do echo a; done; echo x
Could someone explain, why my code produced this example? Checking the man page, it clearly says that the required syntax has to be
for name ... [ in word ... ] term do list done
No shortcut explains that I could leave out the do or done.
I'm running zsh 5.8
Somewhat confusingly, that syntax is not described in the section that introduces the for loop. Instead it's listed with some other short command versions, in a separate part of the zshmisc man page titled ALTERNATE FORMS FOR COMPLEX COMMANDS.
for name ... [ in word ... ] term sublist
where term is at least one newline or ;. Another short form of for.
The introduction to the section mentions some caveats:
Many of zsh's complex commands have alternate forms. These
are non-standard and are likely not to be obvious even to seasoned
shell programmers; they should not be used anywhere that portability
of shell code is a concern.
The short versions below only work if sublist is of the form `{ list
}' or if the SHORT_LOOPS option is set.

How to correctly do line continuation in Julia?

In fortran, we can simply use & to do line continuation.
But I wonder, in Julia, is there a safe way to do line continuation?
I heard that sometimes Julia cannot identify line continuation and it can cause bugs? Because after, Julia does not seem to have any symbol to do line continuation. Can Julia correctly recognize the line continuation?
Like I define the below function with long arguments,
function mean_covar_init(kmix::Int64,dim_p::Int64,weight::Array{Float64,1},sigma::Array{Float64,2},mu::Array{Float64,2})
return nothing
end
If I do things like
function mean_covar_init(kmix::Int64
,dim_p::Int64
,weight::Array{Float64,1}
,sigma::Array{Float64,2}
,mu::Array{Float64,2})
return nothing
end
Is it safe? Thank you very much!
If you do the thing like you have presented it is safe because Julia sees ( in the first line of code so it will look for a closing ).
However a problematic code would be:
f() = 1
+ 2
The reason is that the f() = 1 part is a valid and complete function definition. Therefore you need to make sure to signal Julia that the line is incomplete. The three most typical ways to do it are:
Move the + to the end of first line:
f() = 1 +
2
Use ( and ) as a wrapper:
f() = (1
+ 2)
Use begin and end:
f() = begin 1
+ 2 end
Let me give another example with macros, which do not require parenthesis or punctuation and therefore can be often tricky. Therefore the following:
#assert isodd(4) "What even are numbers?"
if rewritten as
#assert isodd(4)
"What even are numbers?"
does not produce what you expect, and you need to do e.g.:
#assert(isodd(4),
"What even are numbers?")

What is meant by the find man page explanation for the -printf switch?

In the man page for the GNU version of find, at the end of the
EXPRESSIONS: ACTIONS: -printf section, is the following perplexing line:
A '%' at the end of the format
argument causes undefined behaviour since there is no following
character. In some locales, it may hide your door keys, while in
others it may remove the final page from the novel you are
reading.
I like the imagery, but what the does this actually mean? The find utility does not actually allow such a printf argument to be processed:
> find -printf "%"
find: error: % at end of format string
Inspection of the source code for reasonably recent versions of GNU find shows that it checks for this condition (a trailing %) and aborts with the error you indicate, so the excerpt from the man page appears to be nonsense.
I honestly can't imagine it ever behaved otherwise, so I doubt this excerpt was ever true.
My best guess is that the person writing the documentation somehow got it into their head that find's printf formats were somehow passed directly to the C library's printf function (which is ridiculous) and that the C library code might take some bizarre, locale-dependent action (which also seems ridiculous).

In zsh pass hash as parameter

What is the correct / idiomatic way of passing a hash to a function?
I have sort of hit upon this but am not sure how clean this is or if there any pitfalls.
typeset -A hash
hash=(a sometext b moretext)
foo hash
foo() {
typeset -A mhash
mhash=( ${(Pkv)1} )
}
The P flag interprets result (in this case $1 as holding a parameter name). Since this resulted in only getting the values and not the keys, I bolted on the "kv" to get both keys and values.
Is this the correct way, or is there another way. btw, since i am passing an array and a hash in my actual program, I don't want to use "$*" or "$#"
I tried a little and i'm not sure there is an other way than using $# on the function.
Re: Array as parameter - Zsh mailing list
Possible answers in these questions (bash-oriented):
How to pass an associative array as argument to a function in Bash?
Passing arrays as parameters in bash
Passing array to function of shell script
In fact, when you start needing to use an array, or even worse, an associative array in a shell script, maybe it's time to switch to a more powerful script language, like perl or python.
If you don't do it for you, do it for you 6 months from now / for your successors.

Bourne shell scripts with user input

I'm trying to teach myself the basics of Bourne shell scripting using a textbook I borrowed from the library, and I'm working through the questions at the end of each chapter. However, I just got to one and I'm stumped...
Write a script that takes zero or more arguments and prints the last argument in the list. For example, given the argument 'myProgram arg1 arg2 arg3', the output would be 'arg3'.
Could anyone give me some advice on how to set this one up? I'm trying to review the section on user input and arguments, but I haven't worked with that much so far, so I don't have much practice yet.
echo ${!#} # bash only
eval echo \${$#} # sh-compatible
Explanation
The number of arguments is $#. Variables can be accessed indirectly via ${!VAR}. For example:
$ VAR="PATH"
$ echo ${!VAR}
/sbin:/bin:/usr/sbin:/usr/bin
Put those together and if we have a variable $n containing an integer we can access the $nth command-line argument with ${!n}. Or instead of $n let's use $#; the last command-line argument is ${!#}!
Additionally, this can be more longwindedly written using array slicing ($# is an array holding all the command-line arguments) as:
echo ${#:$#:$#}
Oddly, you cannot use an array index:
# Does not work
echo ${#[$#]}
I'll just give you some pointers. Since you want to learn bash, you probably don't just want a piece of code that does what the question asks:
1) Do you know how to count how many arguments your bash function has?
2) Do you know how to loop?
3) Do you know how to "pop" one of the arguments?
4) Do you know how to print out the first argument?
If you put all that together, I bet you'll come up with it.

Resources