Arithmetic in tsch - linux

I am working on updating a script to append the loop number iteration to a filename. However I can't find any documentation that will help me update the variable value. Instead, it appends the operation as a string.
(base) me#axoneme:~$ tcsh
axoneme:~> set j=100
axoneme:~> set j=$j+1
axoneme:~> echo $j
100+1
How can I do arithmetic in tcsh such that the 102nd loop will just be set j= $j + 1 where $j =101

# sign seems to be used in tcsh
axoneme:~> set j = 100
axoneme:~> # j = 100 + 1
axoneme:~> echo $j
101

Related

"Attempted assignment to a non-variable" in bash

I'm new to Bash and I've been having issues with creating a script. What this script does is take numbers and add them to a total. However, I can't get total to work.It constantly claims that total is a non-variable despite it being assigned earlier in the program.
error message (8 is an example number being entered)
./adder: line 16: 0 = 0 + 8: attempted assignment to non-variable (error token is "= 0 + 8")
#!/bin/bash
clear
total=0
count=0
while [[ $choice != 0 ]]; do
echo Please enter a number or 0 to quit
read choice
if [[ $choice != 0 ]];
then
$(($total = $total + $choice))
$(($count = $count + 1))
echo Total is $total
echo
echo Total is derived from $count numbers
fi
done
exit 0
Get rid of some of the dollar signs in front of the variable names. They're optional inside of an arithmetic context, which is what ((...)) is. On the left-hand side of an assignment they're not just optional, they're forbidden, because = needs the variable name on the left rather than its value.
Also $((...)) should be plain ((...)) without the leading dollar sign. The dollar sign will capture the result of the expression and try to run it as a command. It'll try to run a command named 0 or 5 or whatever the computed value is.
You can write:
((total = $total + $choice))
((count = $count + 1))
or:
((total = total + choice))
((count = count + 1))
or even:
((total += choice))
((count += 1))

Shell script, stop running if failed

I have a shell script that runs a test repeatedly :
#!/bin/tcsh
set x = 1
while ($x <= 10000)
echo $x
./test
# x += 1
end
I am trying to adapt it to break the loop and stop running if the test failed, i.e. the test executable returned with a non-zero status. I thought the following change would work.
#!/bin/tcsh
set x = 1
set y = 0
while ($x <= 10000 && $y == 0)
echo $x
# y = ./test
# x += 1
end
But, I get error #: Expression syntax
Can you please tell me what did I do wrong, and how to capture the return value of ./test in a variable so I can break the loop, or some other way to break the loop upon encountering the test failure
I'm not a fan of scripting in csh, and I highly advise against it. However, in this case, csh seems to do the right thing, and you can simply do:
#!/bin/tcsh
set x = 1
while ($x <= 10000)
echo $x
./test || break
# x += 1
end

pbsnodes and bash string matching

I have a string of the form: "8, 14-24, 30-45, 9", which is a substring of the output of pbsnodes. This shows the cores in use on a given node, where 14-24 is a range of cores in use.
I'd like to know the total number of cores in use from this string, i.e.
1 + (24 - 14 + 1) + (45 - 30 + 1 )+ 1 in this example, using a bash script.
Any suggestions or help is much appreciated.
Michael
You could use pure bash techniques to achieve this. By reading the string to array and doing the arithmetic operator using the $((..)) operator. You can run these commands directly on the command-line,
IFS=", " read -ra numArray <<<"8, 14-24, 30-45, 9"
unset count
for word in "${numArray[#]}"; do
(( ${#word} == 1 )) && ((++count)) || count=$(( count + ${word#*-} - ${word%-*} + 1 ))
done
printf "%s\n" "$count"
The idea is
The read with -a splits the string on the de-limiter set by IFS and reads it into the array numArray
A loop on each of the elements, for each element, if the element is just single character, just increment total count by 1
For numeric ranges, do manipulation as e.g. for number a-b use parameter expansion syntax ${word#*-} and ${word%-*} to extract b and a respectively and do b-a+1 and add it with count calculated already and print the element after the loop
You can put this in a bash script with she-bang set to #!/bin/bash and run the script or run it directly from the command-line

Reversing a bash for loop

I have this:
for (( count= "$WP_RANGE_START"; count< "$WP_RANGE_STOP"+1; count=count+1 ));
Where WP_RANGE_STARTis a number like 1 and WP_RANGE_STOPis a number like 10.
Right now this will step though going 1,2,...10
How can I do so that it counts backwards?(10,9,...1)
I guess the mirror image of what you have would be
for (( count="$WP_RANGE_STOP"; count >= "$WP_RANGE_START"; count=count-1 ));
But a less cumbersome way to write it would be
for (( count=WP_RANGE_STOP; count >= WP_RANGE_START; count-- ));
The $ is unnecessary in arithmetic context.
When dealing with literals, bash has a range expansion feature using brace expansion:
for i in {0..10}; # or {10..0} or what have you
But it's cumbersome to use with variables, as the brace expansion happens before parameter expansion. It's usually easier to use arithmetic for loops in those cases.
Your incrementing code can be "simplified" as:
for count in $(eval echo {$WP_RANGE_START..$WP_RANGE_STOP});
So, to decrement you can just reverse the parameters"
for count in $(eval echo {$WP_RANGE_STOP..$WP_RANGE_START});
Assuming you've got a bash version of 3 or higher, you can specify an increment or decrement by appending it to the range, like so:
CHANGE=1
for count in $(eval echo {$WP_RANGE_STOP..$WP_RANGE_START..$CHANGE});
The for loop is your problem.
i=11 ; until [ $((i=i-1)) -lt 1 ] ; do echo $i ; done
OUTPUT
10
9
8
7
6
5
4
3
2
1
You don't need any bashisms at all.

Correcting shell script vulnerabilities

What are some simple fixes you would make for the obvious vulnerabilities in this script?
#!/bin/tcsh
# foreachfile command
# a shell script to apply command to each file in the current directory
set ListOfFiles = `ls`
set Count = 1
set ListLength = $#ListOfFiles
while ($Count <= $ListLength)
$argv $ListOfFiles[$Count]
# Count = $Count + 1
end
#!/bin/tcsh
# foreachfile command <<<< You gave away the ending!
# a shell script to apply command to each file in the current directory
foreach f (*)
"$argv" "$f"
end
You might want to check $argv against a whitelist of permitted commands.

Resources