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
Related
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.
Hey so lets say you have a string "aabbaabbbaab". As you can see you have 3 blocks of "b". For example, how do I remove the 2nd block of b , so "bbb"? It should turn the string into: "aabbaaaab". I have tried looking everywhere but I just couldnt think of a right syntax for my specific question. I need to do this in pure bash so no awk, sed etc.
Here's pure bash: it iterates over the string, character by character. When it detects it's in the n'th block of the specified char, we know that the entire string up til here is the first part of the output we want. When we get to the end of the n'th block, we know that the rest of the string is wanted.
remove_nth_block () {
local str=$1 char=$2 n=$3
local i count=0 prev prefix
for ((i=0; i<${#str}; i++)); do
if [[ ${str:i:1} = $char && $prev != $char ]]; then
((++count == n)) && prefix=${str:0:i}
else
if [[ ${str:i:1} != $char && $prev = $char && $count -eq $n ]]; then
echo "$prefix${str:i}"
return
fi
fi
prev=${str:i:1}
done
}
Then
$ remove_nth_block aabbaabbbaab b 2
aabbaaaab
$ remove_nth_block aabbaabbbaab a 2
aabbbbbaab
This should print myString after replacing all occurrences of bbb with nothing. For some really useful tips and examples of string manipulation in bash, check out this site.
myString="aabbaabbbaab"
echo ${myString//bbb/}
I need to determine which of three strings is found in a file. It is guaranteed that only one of the three is found in the file. Then I want to do a different thing based on which of the three is in the file.
I am currently trying to do:
myfile="/home/directory/file.xml"
case "stringOne" in
*$myfile*)
#Do thing A
;;
esac
case "stringTwo" in
*$myfile*)
#Do thing B
;;
esac
case "stringThree" in
*$myfile*)
#Do thing C
;;
esac
however, this is not working, and my program gets stuck. Is there a better way, or a quick way to fix this way?
Since the file is guaranteed to contain one of them, you can find which one it is and the use case statements:
myfile="/home/directory/file.xml"
str=$(grep -o -m 1 -E 'stringOne|stringTwo|stringThree' $myfile)
[[ -z ${str} ]] && { echo "No match found!"; exit 1; }
case "${str}" in
stringOne)
#Do thing A
;;
stringTwo)
#Do thing B
;;
stringThree)
#Do thing C
;;
esac
grep options:
-o will print only the matching word. So you know which word is there in the input file.
-m 1 will ensure it stops at first match. So it doesn't need to scan the rest of the file.
-E is for regex match that match which is to match either one of the three strings.
You can read the file data once and then check each string's presence:
data=$(</home/directory/file.xml)
if [[ $data == *stringOne* ]]; then
echo "process stringOne"
elif [[ $data == *stringTwo* ]]; then
echo "process stringTwo"
elif [[ $data == *stringThree* ]]; then
echo "process stringThree"
fi
I'm trying to implement a function in bash which displays a tree of files/directories for the given depth. It takes 3 arguments.
$1 = *current directory*
$2 = *current depth*
$3 = *lines*
for example, if my current directory is ".../living/", my depth is 2, my function should output:
DIR .../living/
----DIR animals
--------FILE dog
--------FILE cat
----DIR plants
--------FILE flowers
As you can see, the number of lines is increased by 4 for each depth change. The type of file (DIR, FILE) is not the question of this thread.
Here's what I have so far:
function tree {
#some code to get the directory in variable cwd
...
a=$(getType $cwd)
echo "$a $cwd"
depth=3 #the value does not matter, it's just for you guys to see
drawTree $cwd $depth "----"
}
function drawTree {
if [[ $2 == 0 ]]; then
return
fi
dat=$1
list=$(ls $dat)
depth=$2
lines=$3
for d in $list; do
f="$dat/$d"
t=$(getType $f)
echo "$lines$t $d"
if [[ $t == "DIR" ]]; then
g=$(($depth-1))
l="$lines----"
if [[ $g > 00 ]]; then
drawTree $f $g $l
fi
fi
done
The output of this code is sadly false and I have no idea why.
There are quite a few issues with that code.
The most serious is that your variables are not made local (see help local) which can be disastrous in a recursive function. In the loop in drawtree, the second iteration will see unwanted modifications to $depth and $lines, both of which will cause the output to be incorrect in different ways.
Also:
g=$(($depth-1))
l="$lines----"
if [[ $g > 00 ]]; then
drawTree $f $g $l
fi
would much better be written without so many unnecessary variables, and using arithmetic rather than string comparison:
if (( depth > 1 )); then
drawTree $f $((depth - 1)) ${lines}----
fi
Finally:
list=$(ls $dat)
for d in $list; do
will fail disastrously if there is whitespace or a shell metacharacter in a filepath. Much better is the use of a bash array and glob expansion rather than the ls command):
# Create an array from a glob
list=("$dat"/*)
# Use the elements of the array, individually quoted:
for d in "${list[#]}"; do
I need some help from this awesome community.
I'm trying to write a script that loops through each word of a sentence stored in a variable (for example SENTENCE).
Example:
for WORD in $SENTENCE
do
echo do something
done
The problem that I'm facing is that I need to change the value of WORD to restart the loop if a certain condition is true inside the loop.
Example:
for WORD in $SENTENCE
do
echo do something
if [[ $SOMETHING_HAPPENED == TRUE ]]; then
WORD=$FIRST_WORD_IN_SENTENCE
fi
done
Basically, I need to restart the loop if certain conditions are met (SOMETHING_HAPPENED), but I don't know how to do this properly.
If this was a normal C loop I would do it like this:
for (i=0;i<10;i++){
do_something();
if (SOMETHING_HAPPENED == TRUE){
i=0;
}
}
How do I do this in shell script?
Thank you.
You could use a loop around your for loop that executes the for loop until the something doesn't happen:
repeat=yes
while [ "$repeat" = yes ]; do
repeat=no
for WORD in $SENTENCE; do
# do something
if [[ $SOMETHING_HAPPENED == TRUE ]]; then
repeat=yes
break
fi
done
done
while [[ 1 ]]; do # infinite loop
for word in $sentence; do
…
if [[ $condition ]]; then
continue 2 # go to the next iteration of the *outer* loop
fi
done
break # escape the outer loop
done
To restart the loop you can use BASH arrays and run your loop like this:
arr=( $sentence )
for ((i=0; i<${#arr[#]}; i++)); do
word="${arr[$i]}"
# evaluate condition
if [[ "$SOMETHING_HAPPENED" == TRUE ]]; then
i=0
continue
fi
echo "$word"
done