I've been tasked with re-writing this in bash. But whilst most powershell is easy to read, i just dont get what this block is actually doing!!? Any ideas?
It takes a file which is sorted on a key first, maybe thats relevant!
Thanks for any insights!
foreach ($line in $sfile)
{
$row = $line.split('|');
if (-not $ops[$row[1]]) {
$ops[$row[1]] = 0;
}
if ($row[4] -eq '0') {
$ops[$row[1]]++;
}
if ($row[4] -eq '1') {
$ops[$row[1]]--;
}
#write-host $line $ops[$row[1]];
$prevrow = $row;
}
Perhaps a little refactoring would help:
foreach ($line in $sfile)
{
# $row is an array of fields on this line that were separated by '|'
$row = $line.split('|');
$key = $row[1]
$interestingCol = $row[4]
# Initialize $ops entry for key if it doesn't
# exist (or if key does exist and the value is 0, $null or $false)
if (-not $ops[$key]) {
$ops[$key] = 0;
}
if ($interestingCol -eq '0') {
$ops[$key]++;
}
elseif ($interestingCol -eq '1') {
$ops[$key]--;
}
#write-host $line $ops[$key];
# This appears to be dead code - unless it is used later
$prevrow = $row;
}
You are splitting a line on the '|' charater to an array row. It looks like you are using the $row array as some type of key into the $ops var. The first if test to see if the object exists if it doesn't it creates it in $ops the second and third ifs test to see if the 5th element in $row are zero and one and either increment or decrement the value created in the first if.
Approximately:
#!/bin/bash
saveIFS=$IFS
while read -r line
do
IFS='|'
row=($line)
# I don't know whether this is intended to test for existence or a boolean value
if [[ ! ${ops[${row[1]}] ]]
then
ops[${row[1]}]=0
fi
if (( ${row[4]} == 0 ))
then
(( ops[${row[1]}]++ ))
fi
if (( ${row[4]} == 1 ))
then
(( ops[${row[1]}]-- ))
fi
# commented out
# echo "$line ${ops[${row[1]}]}
prevrow=$row
done < "$sfile"
Related
I have a text file with comma delimiter like following
for example str_data.txt
aaa,111,bbb
ccc,222,ddd
eee,333,fff
I have a bash function to validate each token (i.e. if each token is following some rule or not based on that function will echo true or false. (can leave it like [[ XYZ == "$1"]] also, instead of returning echo) )
for example
function validate_token {
local _rule = XYZ
if [[ XYZ == "$1" ]]; then
echo "true"
else
echo "false"
fi
}
I want to write a bash script (one-liner or multi-line) to validate all these tokens separately (i.e. validate_token "aaa" then validate_token "111") and finally answer "true" or "false" based on ANDing of each token's results.
Would yo please try the following:
validate_token() {
local rule="???" # matches a three-chraracter string
if [[ $1 == $rule ]]; then
echo 1
else
echo 0
fi
}
final=1 # final result
while IFS=',' read -ra ary; do
for i in "${ary[#]}"; do
final=$(( final & $(validate_token "$i") ))
# take AND with the individual test result
done
done < "str_data.txt"
(( $final )) && echo "true" || echo "false"
I've also modified your function due to several reasons.
When defining a bash function, the form name() { .. } is preferred.
It is not recommended to start the user's variable name with an underscore.
You have localized it and don't have to care about the variable name
collision.
When evaluating the conditional expression by using == or = operator
within [[ .. ]], it will be better to place the pattern or rule to the right of the
operator.
It will be convenient to return 1 or 0 rather than true or false for further calculation.
Hope this helps.
You can try the below, reading line by line and storing the values into an array, then iterating the array calling the function for each value :
IFS=","
while read line
do
read -ra lineValues <<< "$line"
for value in "${lineValues[#]}"
do
validate_token "$value"
done
done < your_txt_file
I'm trying to learn how to use arrays in bash. I'm writing a script that asks the user for three numbers and figures out what the biggest number is. I want the script to force the user to enter only numeric values. Furthermore, I want the three numbers should be different. I'm using an array to store user input. This is what I have so far:
## declare variables
order=("first" "second" "third")
answers=()
RED='\033[0;31m'
NC='\033[0m' # No Color
numbersonly() {
if [[ ! $1 =~ ^[0-9]+$ ]]; then
echo "${RED}$1 is not a valid number.${NC}"
else
answers+=("$input")
break
fi
}
inarray(){
for e in ${answers[#]}; do
if [[ $1 == $e ]]; then
echo "${RED}Warning.${NC}"
fi
done
}
readnumber(){
for i in {1..3}; do
j=$(awk "BEGIN { print $i-1 }")
while read -p "Enter the ${order[$j]} number: " input ; do
inarray $input
numbersonly $input
done
done
}
displayanswers(){
echo "Your numbers are: ${answers[#]}"
}
biggestnumber(){
if (( ${answers[0]} >= ${answers[1]} )); then
biggest=${answers[0]}
else
biggest=${answers[1]}
fi
if (( $biggest <= ${answers[2]} )); then
biggest=${answers[2]}
fi
echo "The biggest number is: $biggest"
}
main(){
readnumber
displayanswers
biggestnumber
}
main
Right now, I can get the script to display a warning when the user enters a number that was previously entered, but I can't seem to find the proper syntax to stay in the while loop if the user input has already been entered. Thoughts?
I found a way around it. My problem was twofold: 1) I didn't realize that if you have a for loop nested in a while loop, you'll need two break statements to exit the while loop; 2) having two functions within the same while loop made it hard to control what was happening. By merging inarray() and numbersonly() into a new function, I solved the double conditional issue. The new function looks like this:
testing(){
for item in ${answers[*]}
do
test "$item" == "$1" && { inlist="yes"; break; } || inlist="no"
done
if [[ $inlist == "yes" ]]; then
echo "${RED}$1 is already in list.${NC}"
else
if [[ ! $1 =~ ^[0-9]+$ ]]; then
echo "${RED}$1 is not a valid number.${NC}"
else
answers+=("$input")
break
fi
fi
}
Without much study here is what leapt off the screen to me follows. Beware I haven't actually tested it... debugging is an exercise left to the student.
Recommend using newer "function" definitions as you can declare local variables. () definitions do not allow localized variables.
function inarray
{
local e; #don't muck up any variable e in caller
...
}
To calculate values avoid extra awk and use j=$(( i - 1 ));
biggestnumber should likely use a loop.
Overall comment:
nummax=3; #maximum value defined in just one place
# loop this way... showing optional {} trick for marking larger loops
for (( n = 0; n < nummax; ++n )); do
{
nx=$(( 1 + n )); #1 based index
} done;
Hint: should stop input loop once all input present. Could also add:
if [ "" == "${input:-}" ]; then break;
for (( a = 0; a < ${#answer[*]}; ++a )); do
Note the extensive use of double quotes to avoid syntax errors if the variable value is empty or contains many shell metacharacters, like spaces. I can't tell you how many bug reports I've fixed by adding the quotes to existing code.
[[ ... ]] expressions use file name tests, not regular expressions. The closest you can get to [[ ! $1 =~ ^[0-9]+$ ]]; is using [[ "$1" != [0-9]* ]] && [[ "$1" != *[^0-9]* ]].
But I suspect ! expr >/dev/null "$i" : '[0-9][0-9]*$'; is more what you want as "expr" does use regular expressions. Don't enclose in []s. Used [0-9][0-9]* rather than [0-9]+ as "+" has given me mixed successes across all dialects of UNIX regular expressions.
Here is my situation. I have an array of Device id's. I know which device id's go with which ip addresses. I want to use the device id's to find the correct ip address and pass both of them in a command. I am using a shell script to do this.
Here is some psuedo code for what I want.
This is my dictionary type thing matching id's with ip's, no particular order
dictionary_thing = ( id:ip
id:ip
id:ip
id:ip )
This is my array of id that I am using in no particular order
array_of_used_ids = ( id
id
id )
Maybe make an array of objects with two properties (id and ip) for each id found
array_object
for(int i=0; i<count; i++)
{
array_object[i]= new object and set id to dictionary_thing id and ip
}
then I want to run all the commands in a for loop (or whatever you suggest)
for id in array_of_used_ids
./run_this_command with
for(int i=0; i<count; i++)
{
./command array_object[i].ip array_object[i].id
}
You already have your primary arrays built, so build a regex from the used IDs
regex="^${array_of_used_ids[0]}:"
i=1
while [ $i -lt ${#array_of_used_ids[*]} ]; do
regex="$regex|^${array_of_used_ids[$i]}:"
i=$(( $i + 1))
done
This should give you a regex like "^id:|^id:|^id:"
Then iterate your dictionary checking each against the regex, when matched, replace the separating ':' with a space
array_object=()
i=0
j=0
while [ $i -lt ${#dictionary_thing[*]} ]; do
if [[ $dictionary_thing[$i] =~ $regex ]]; then
array_object[$j] = ${dictionary_thing[$i]/:/ } #if it is possible for an ID to contain a ':', you will need a different method for splitting here
j=$(( $j + 1 ))
fi
i=$(( $i + 1 ))
done
Finally, iterate your result object and execute the command
i=0
while [ $i -lt ${#array_object[*]} ]; do
command_to_execute ${array_object[$i]}
i=$(( $i + 1 ))
done
Thank you very much for helping!!!
I have the following code:
base[0]='coordfinder'
base[1]='editor_and_options'
base[2]='global'
base[3]='gyro'
base[4]='movecamera'
base[5]='orientation'
base[6]='sa'
for d in $dest_include/*; do
if [ $d == "${base[#]}" ]; then
echo $plugin='y' >> vt_conf.sh
else
plugin=$(basename $d)
echo $plugin'?'
read $plugin
echo $plugin=${!plugin} >> vt_conf.sh
fi
done
It doesn't work, but it's a good starting point.
Basically the thing that doesn't work is the if loop. I just made it up because I don't know how to do it.
I'd like to do the following:
Loop through the content of $dest_include folder.
If any of the forlders ($d) matches any of the elements in the array do one thing, else do something else.
Thanks!!!
Iterate through an inner loop, setting a flag if you find a match.
base=( coordfinder editor_and_options global gyro movecamera orientation sa )
for d in "$dest_include/"*; do
found_match=0
for i in "${base[#]}"; do
[[ $d = "$i" ]] && { found_match=1; break; }
done
if (( found_match )) ; then
...do one thing...
else
...do the other...
fi
done
you could also turn the check around: use the whole array as a whitespace-separated string, and try to match a whitespace-delimited word within it.
for d in "$dest_include"/* do
if [[ " ${base[*]} " == *" $(basename "$d") "* ]]; then
do something with matching directory
else
do another thiing
fi
done
Please explain me about how to use unix shell function correctly.
For example we have following functions f and g:
f()
{
#do something
return $code
}
g()
{
print $something
}
We can use function f in the next way:
f
if [[ $? -eq 0 ]]; then
#do 1
else
#do 2
fi
This function performs some work and exits with some exit status.
And we can analyze this exit status.
We can use function g in the next way:
g
or
result=$(g)
if [[ $result = "something" ]]; then
#do something
fi
In first case we just called function.
In the second case we used command substitution to assign all text that function prints to stdout to variable result.
But what if there is following function:
z()
{
user=$1
type=$2
if [[ $type = "customer" ]]; then
result=$(/somedir/someapp -u $user)
if [[ $result = "" ]]; then
#something goes wrong
#I do not want to continue
#I want to stop whole script
exit 1
else
print $result
fi
else
print "worker"
fi
}
I can use function z in the next way:
z
If something goes wrong then whole script will be stopped.
But what if somebody uses this function in command substitution:
result=$(z)
In this case if someapp returns empty string script will not be stopped.
Is it not correct approach to use exit in functions?
I don't have a way to test this right now, but ksh (maybe bash too), can scope variables inside functions.
z()
{
typeset result
user=$1
type=$2
if [[ $type = "customer" ]]; then
result=$(/somedir/someapp -u $user)
if [[ $result = "" ]]; then
#something goes wrong
#I do not want to continue
#I want to stop whole script
exit 1
else
print $result
fi
else
print "worker"
fi
}
Notice the insertion of typeset result near the top.
You may need to use the alternate declartion of function for this feature to work, i.e.
function z {
#....
}
I hope this helps.
You could also do something like
result=$(z ; "eval retCode=\$? ; echo \$retCode" )