How to work with command line arguments in J - j

In J console mode if I type ARGV I get the full path of jqt.exe But when I try to pass some strings to a J script file I get 'syntax error' or 'domain error'. How does argument passing and retrieval or display work?

If you want to write to a file you would pass the information using
'string' 1:!2 'filepath/jscriptfile'
see https://www.jsoftware.com/help/dictionary/dx001.htm
if you want to pass an argument to a verb declared in a script, you would first have to load the script
load 'filepath/jscriptfile'
Then as long as the script contains verbs that have been assigned using =: so that the verb is not local to the script file, you would pass the string to the verb, which has now been loaded.
verb 'string'

An interaction with a script that just prints out its arguments:
$ cat args.ijs
#! /usr/bin/env j
exit echo each ARGV
$ ./args.ijs
j
./args.ijs
$ ./args.ijs 1 2 3
j
./args.ijs
1
2
3
$ ./args.ijs '1 2' 3
j
./args.ijs
1 2
3
ARGV is a list of the boxed arguments to the script. It works like any list of boxed literals, and if you're a domain error it's from some verb in your script that's given arguments it's not defined to handle. If you're getting a syntax error it's because there's something in your script with incorrect syntax. This is unrelated to ARGV as such.
Perhaps you're expecting numerical arguments to be numbers? Arguments are always delivered as strings. Here's a slightly more involved script with usage, that prints the sum of the factorials of its arguments:
#! /usr/bin/env j
sumfact =: [: +/ [: ! x:
3 : 0''
if. (#ARGV) > 2 do.
echo sumfact > 0&". each 2}.ARGV
exit 0
else.
echo 'usage: ', (1{::ARGV), ' <n1> [<n2> ... <nn>]'
exit 1
end.
)
As used:
$ ./sumfact.ijs
usage: ./sumfact.ijs <n1> [<n2> ... <nn>]
$ ./sumfact.ijs 0
1
$ ./sumfact.ijs 5
120
$ ./sumfact.ijs 5 5 5
360
$ ./sumfact.ijs 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
The text after #! isn't important; I use /usr/bin/env j because I have a j in my path that's the usual bin/jconsole of a J installation.

Related

Is there a way to pass multiple argument as a single string in bash?

I'm running a program that takes variable numbers of arguments with the same flag. For example
myprogram -args 'var1' 'var2' 'var3' 'var4'
myprogram -args 'var5' 'var6'
I have to launch this program several times with different sets of arguments provided in a test.txt file.
arg1 arg2 arg3
arg5 arg6
arg7
arg8 arg9 arg9 arg10
The program must be inside its own script to request resources in our HPCC.
while read p; do
launchmyprogram.sh "$p"
done < test.txt
I know I can use var1=$1 syntax inside launchmyprogram.sh to collect and allocate the variables, but this cannot handle variable number of arguments, and I'd have to create a script for each line. Is there a way to create a script in bash that takes variable numbers of arguments?
Use arrays to store dynamically sized sequences of strings. Bash has two ways of reading input into an array:
readarray -t somearray turns lines of an entire input file into array elements.
read -a somearray turns tokens of a single line of input into array elements.
In this case you can use the latter. Here’s a runnable MWE:
myprogram() {
local -i i
echo "Got ${#} arguments."
for ((i = 1; i <= $#; ++i)); do
echo "Argument No. ${i} is '${!i}'."
done
}
while read -ra args; do
myprogram -args "${args[#]}"
done <<-INPUT
arg1 arg2 arg3
arg5 arg6
arg7
arg8 arg9 arg9 arg10
INPUT
That way the arguments from each line are kept separate, as the output suggests:
Got 4 arguments.
Argument No. 1 is '-args'.
Argument No. 2 is 'arg1'.
Argument No. 3 is 'arg2'.
Argument No. 4 is 'arg3'.
Got 3 arguments.
Argument No. 1 is '-args'.
Argument No. 2 is 'arg5'.
Argument No. 3 is 'arg6'.
Got 2 arguments.
Argument No. 1 is '-args'.
Argument No. 2 is 'arg7'.
Got 5 arguments.
Argument No. 1 is '-args'.
Argument No. 2 is 'arg8'.
Argument No. 3 is 'arg9'.
Argument No. 4 is 'arg9'.
Argument No. 5 is 'arg10'.
You can use $# to query the number of arguments passed to launchmyprogram.sh. Something like
if [ $# -eq 1 ]; then
echo "one argument"
elif [ $# -eq 2 ]; then
echo "two arguments"
elif [ $# -eq 3 ]; then
echo "three arguments"
else
echo "too many arguments"
exit 1
fi
All bash scripts take a variable number of arguments, your question is about how to access them. The simplest method is:
for arg; do
my-cmd "$arg"
done
This repeats my-cmd with each argument, individually. You can use this loop in launchmyprogram.sh. You can also put the relevant code in a function, and use just the function inside the loop.
Parsing arguments from a file is more complicated. If the arguments aren't quoted for the shell, and don't contain spaces or wildcard characters ([]?*), you could just unquote $p in your example. It will be split on white space in to multiple arguments.
In this case you could also just parse the whole file in launchmyprogram.txt:
for arg in $(<test.txt); do
my-cmd "$arg"
done
This basically is Andrej's answer, but to simplify and make it just a little more directly related to the structure of the question -
while read -ra p; do # read and parse line into an array
myprogram -args "${p[#]}" # pass elements as separate values
done < test.txt # after reading them in as one line

Using command-line arguments in J

I know that ARGV represents the arguments. Can I type arguments after the filename in jqt.exe or does that only work in jconsole.exe. Executing the code regarding ARGV given in the J docs crashes J on my Win 10. An example would be welcome.
I tried out 0 ". > ,. 2 }. ARGV from an earlier question on SO, but am not sure how to make it work.
Both jqt and jconsole read the command line arguments and box them:
jqt script.ijs arg1 arg2
ARGV
┌───┬──────────┬────┬────┐
│jqt│script.ijs│arg1│arg2│
└───┴──────────┴────┴────┘
2}. ARGV
┌────┬────┐
│arg1│arg2│
└────┴────┘
] x =: > 3 { ARGV
arg2
example script:
$ cat script.ijs
x =: ". every 2 }. ARGV
echo +/ x
$ jqt script.ijs 1 2 3
6

Bash, print 0 in terminal each time a non recognised argument is input

I have a bash program which extracts marks from a file that looks like this:
Jack ex1=5 ex2=3 quiz1=9 quiz2=10 exam=50
I want the code to execute such that when I input into terminal:
./program -ex1 -ex2 -ex3
Jack does not have an ex3 in his data, so an output of 0 will be returned:
Jack 5 3 0
how do I code my program to output 0 for each unrecognized argument?
If I understand what you are trying to do, it isn't that difficult. What you need to do is read each line into a name and the remainder into marks. (input is read from stdin)
Then for each argument given on the command line, check if the first part matches the beginning of any grade in marks (the left size of the = sign). If it does, then save the grade (right side of the = sign) and set the found flag to 1.
After checking all marks against the first argument, if the found flag is 1, output the grade, otherwise output 0. Repeat for all command line arguments. (and then for all students in file) Let me know if you have questions:
#!/bin/bash
declare -i found=0 # initialize variables
declare -i grade=0
while read -r name marks; do # read each line into name & marks
printf "%s" "$name" # print student name
for i in "$#"; do # for each command line argument
found=0 # reset found (flag) 0
for j in $marks; do # for each set of marks check for match
[ $i = -${j%=*} ] && { found=1; grade=${j#*=}; } # if match save grade
done
[ $found -eq 1 ] && printf " %d" $grade || printf " 0" # print grade or 0
done
printf "\n" # print newline
done
exit 0
Output
$ bash marks_check.sh -ex1 -ex2 -ex3 < dat/marks.txt
Jack 5 3 0

Require efficient and probably one to two liner solution to replace space

Having following bash scripts to find index of the command from command list
#!/bin/bash
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
cmdlist="${cmdlist//,/ }" #To Replace , with space in array list
cmdlist="${cmdlist//cmd/ }" #To get index of command
echo $cmdlist //Added for clarification
for a in $cmdlist
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
As you can see how I extract command index from command list but it's failing (of course it fails) in some condition. So want make some validations as follows:
1) In list, if argument is cmd then it will skipped and not replace space instead cmd string because argument length must be greater then 3.
2) In list, if argument is cmd0001 then also skipped and not replace space instead cmd string because argument length must be less or equal 5 and greater then 3.
Following above validation I achieved by taking for..loop, taking temporary array then compare each argument and validate then store in temporary array and finally copy temporary array in original one.So this is too long procedure.
Any one have idea for batter solution?
Like
cmdlist="${cmdlist//cmd/ }" #To get index of command
command only replace space instead cmd in target argument if condition [[ length -gt 3 ]] && [[ length -le 5 ]] match.
Note: have already have solution using for..loop.
UPDATE: Added more detail for what i want
I got output this from script
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
but i want this
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : cmd000001
invalid command index : cmdxyz
So basically leave the argument which not in validation range and mark as invalid index(nothing to do not require to replace space in place of cmd string.)
More UPDATE: Again added more detail to clarify exactly what i want
Have added one echo statement before for..loop in my script ( see modified above script ) which give me output like this
1 2 24 25 4 10 9 000001 xyz
but i want
1 2 cmd 24 25 4 10 9 cmd000001 cmdxyz
means leave argument as it is if it violate validation like in my list third argument is cmd.It violate the condition becasue it's length not greater then 3.Now see last two argument in list cmd000001,cmdxyz It violate the condition because it's length greater then 5. Valid argument is one for which length must be greater then 3 && less or equal 5.
Hope this will clarify what i want.
Editing as per your update:
Get the values in an array and check within the loop if those meet the required criteria:
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
IFS=, read -a arr <<< "$cmdlist"
for a in "${arr[#]}"
do
v="${a/cmd/}"
if ((v > 50)) || ((v <= 0))
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $v
fi
done
For your input, it'd produce:
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : cmdxyz
Old answer:
Instead of attempting to replace , with spaces and so on, read the string delimited by comma into an array. Manipulate the array to get one containing the desired strings.
$ IFS=, read -a arr <<< "$cmdlist"
$ foo=(${arr[#]/cmd/})
$ for i in "${foo[#]}"; do echo $i; done
1
2
24
25
4
10
9
000001
xyz
$
Checking...
for a in "${foo[#]}"
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
produces:
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
A note of caution: Numbers with leading zeros (as you have in your example) would be considered as octal and might produce unexpected results:
$ [[ 0024 -gt 22 ]] && echo greater || echo smaller
smaller

how to check digit is even or not in linux shell?

i tried
pancmite#atlas:~$ test ($LSD / 2) -eq 0
bash: syntax error near unexpected token `$LSD'
pancmite#atlas:~$ test $LSD / 2 -eq 0
bash: test: too many arguments
pancmite#atlas:~$ test $number -lt $LSD
bash: test: -lt: unary operator expected
i want one line command.
expr $LSD % 2
or alternatively:
$((LSD % 2))
A returned value of 1 is odd and 0 is even. So you could try this as a test:
LSD=5
echo $((LSD % 2))
and you should get a returned value of 1.

Resources