LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   How to compare the user input in bash scripting? (https://www.linuxquestions.org/questions/programming-9/how-to-compare-the-user-input-in-bash-scripting-4175660158/)

bmxakias 09-01-2019 11:49 AM

How to compare the user input in bash scripting?
 
Hello

I have some folders like:


Code:

/backup/$DOMAIN/$DATE/database//inc/inc1
/backup/$DOMAIN/$DATE/database/inc/inc2
/backup/$DOMAIN/$DATE/database/inc/inc3

I am using this code:

Code:

    echo ""
    echo "Available increamental backups :"
    echo ""

PS3="

Please select the incremental backup point to restore [1, 2, 3 e.t.c]:"
# get the folders count
count=$(ls -1td $INCDIR/*/ | wc -l)

select domainbackupdate in $(ls -1td $INCDIR/*/ | awk -F/ '{print $(NF-1)}'); do
  # if input is a number and is less than or equal to count
  if (( $REPLY && $REPLY <= $count )) 2> /dev/null; then
    break
  else
    echo "Your input ${REPLY} is not available. Please select a number from the list:"
  fi
done

    echo "Please wait..." ;;

It does check in the folder $INCDIR/*/ all folders and shows them by order (newest first) like:

Code:

Available increamental backups :

1) inc3
2) inc2
3) inc1


Now what i would like to do is to get the input from the user selection (probably using the read command? ) and if the user select:

1 to run a command for folder inc1
2 to run a command for folder inc1 and then inc2
3 to run a command for inc1 and then inc2 and then inc3

But the problem is that what i will have in 1) or 2) or 3) may not be always the same...

So i must be able to read if it is just inc1 and run a simple cmmand or inc3 and run the 3 needed commands....

Is it possible to do that?

To work it must go from inc1 up to selected inc(x) that the user will select..

Thank you

Firerat 09-01-2019 11:58 AM

yeah, you need to use a bash builtin called select.

We did some crafty things with that the other day
I'll find the link and edit this post for you


Edit
https://www.linuxquestions.org/quest...ds-4175659851/

you should get what you need from that

post what you come up if you get stuck

michaelk 09-01-2019 12:09 PM

Use $domainbackupdate. Its value will be one of the items in the output of the ls command i.e. inc1,inc2 or inc3 in your posted example. So the solution is easier then you think.

There are lots of string manipulation tools to separate the number from inc and then you can use a loop to go from inc1 to the selected inc(x).

https://bash.cyberciti.biz/guide/Select_loop

rnturn 09-01-2019 12:29 PM

Quote:

Originally Posted by bmxakias (Post 6032022)
Code:

Please select the incremental backup point to restore [1, 2, 3 e.t.c]:"
# get the folders count
count=$(ls -1td $INCDIR/*/ | wc -l)

select domainbackupdate in $(ls -1td $INCDIR/*/ | awk -F/ '{print $(NF-1)}'); do
  # if input is a number and is less than or equal to count
  if (( $REPLY && $REPLY <= $count )) 2> /dev/null; then
    break
  else
    echo "Your input ${REPLY} is not available. Please select a number from the list:"
  fi
done


You can use "select" but I've always found that to be an ugly way to go if there are a lot of options. I normally like to make an interface like that screen oriented and read everything that would be a possible option into an array and calculate indices that provide a window into the array and then display only as many as the user's terminal size can hold reserving space for prompting and reading the user's input, and reminders about the "previous screen" and "next screen" options. Takes a bit more effort to code but I think it's more user friendly. Depends on your end-user's needs. When I last used the method above, I was working with SQL developers who a.) sometimes had directories that often had hundreds datasets that they needed to select from for testing and b.) an aversion to organizing their data into logical subdirectories with smaller numbers of files in each. They appreciated the screen-oriented approach rather than having to scroll through a long list of options.

I'll hunt for a simple example of this approach and post it if anyone's interested in seeing it. (Right now I'm off to the hardware store to polish of couple of tasks in the Job Jar. :^) )

bmxakias 09-01-2019 01:03 PM

I think that all solutions are related on how to get the user input and act on that....but on my case i need the script to understand the input number of the folder name like inc3 and then then to run a command for inc1 and then for inc2 and then for inc3 and then stop.

Maybe my knowledge is limited on that but i can't see on the provided links that functionality ... Maybe i didn't explain it well ? :(

Thanks for your replies !

bmxakias 09-01-2019 01:25 PM

To explain it a bit better...

If user select the folder inc1 that can have any number at the selection menu i would like to run this command:

Code:

touch /backup/$DOMAIN/$DATE/database/inc/inc1/file.txt
If user select for example the folder inc3 that can have any number at the selection menu i would like to run this:

Code:

touch /backup/$DOMAIN/$DATE/database/inc/inc1/file.txt
touch /backup/$DOMAIN/$DATE/database/inc/inc2/file.txt
touch /backup/$DOMAIN/$DATE/database/inc/inc3/file.txt

Thank you !

Firerat 09-01-2019 01:52 PM

in the link I posted we talked about how to decide what to do once the user had selected the option

so if user selects inc3
to recreate that you first restore inc1, then inc2 and finally inc3

if user selects inc2
to recreate that you first restore inc1 and finally inc2

if user selects inc1
restore inc1


ok

you built an array starting at 0
0=inc1
1=inc2
2=inc3

then with select presented it as

1) 2=inc3
2) 1=inc2
3) 0=inc1

Code:

Count of backups is 3

REPLY=1 ( 2=inc3 )
Count-REPLY = 2 .. 3 - 1 = 2
for 0 1 2;do
  restore incX
done


REPLY=2 ( 1=inc2 )
Count-REPLY = 1 .. 3 - 2 = 1
for 0 1;do
  restore incX
done

REPLY=3 ( 0-inc1 )
Count-REPLY = 0 .. 3 - 3 = 0
for 0;do
  restore inc
done

so, unchecked code

Code:

Dirs=## array comes from code I wrote in the other thread
# which needs check for gaps
# it is a lil dumb

for ((i=0;i<$(($Count - $REPLY));i++));do
    restore "${Dirs[i]}"
done


does that make sense?
Note : /!\ things like that are out by 1 if they are wrong ;)

Firerat 09-01-2019 03:08 PM

Code:

#!/bin/bash
Dir="$1"

[[ $Dir == test ]] \
    && mkdir -p testing/inc{1..3} \
    && Dir="testing/inc"
# Edit1: I had Dir being set to testing all the time
# Doh!
Dirs=()

for i in ${Dir}*;do
    [[ ${i} =~ ${Dir}[0-9]+ ]] && Dirs+=("${i}")
done

for (( i=$(( ${#Dirs[@]} - 1)) ; i >= 0; i-- ));do
    SelectDir+=("${Dirs[i]}")
done

select CHOICE in "${SelectDir[@]}";do
    [[ "$CHOICE" ]] && break
done

for ((i=0;i <= $((${#Dirs[@]} - $REPLY));i++));do
    echo restore "${Dirs[i]}"
done

but check what happens if you have more than 9 dirs!!

michaelk 09-01-2019 03:31 PM

If you choose inc3 the value of $domainbackupdate will equal inc3. Using a substring function you can assign a variable to equal 3 in this case.

From there you can create a loop from 1 to 3 to create the file.txt files in each incx directory without knowing the order of the select menu.

Firerat 09-01-2019 04:16 PM

yeah true


Code:

select CHOICE in "${Dirs[@]}";do
  [[ "$CHOICE" ]] && break
break
echo "$REPLY"
echo "$(( $REPLY - 1 ))"
echo "${Dirs[$(( $REPLY - 1 ))]}"
echo "${Dirs[$(( $REPLY - 1 ))]##*/}"
getnumber="${Dirs[$(( $REPLY - 1 ))]##*/}
getnumber="${getnumber//[^[:digit:]]/}"
echo $getnumber

if the array is in numerical order you just need to know which direction to walk

you can get the digit from the string and reconstruct, but you already have all that
bash will have globbed in numerical order

reconstructing negates the need for 000pad
but the order things are presented in will look messy
000pad and reconstruct needs more work to strip the leading 0s ( 08 != 8 )

gaps ( inc1 inc3 # inc2 is missing ) will break .. well both I guess
the reconstructed won't find inc2
with the array the restore will fail ( I assume inc3 depends on inc2)

This is where metadata needs to be put in the backups,
a first pass checks the dependencies and then restores once it confirms they are satisfied

which makes it even easier, since inc3 we check inc3/metadata and it says well I need inc2 first, inc2/metadata says well, I need inc1 first

so we only need to run restore on "${Dirs[$(( $REPLY - 1 ))]}"

does that make sense?

bmxakias 09-02-2019 04:44 AM

Thanks for your replies !!!!

The inc folders may be up to 46 ....

Can you please adjust your code for that so i can see the difference ?


Also if it is easier to do it by changing anything in the paths names, it's ok to do that...

For example if 1inc, 2inc or 1-inc, 2-inc is better for the script.....

Firerat 09-02-2019 05:29 AM

you need to pad the 1 2 3 with leading zeroes 001 002 003 .. 999 1000 /!\ 1000 breaks it

you could use printf to pad
revisit the creation part
https://www.linuxquestions.org/quest...me-4175660121/


This is inspired by GazL's post

I added option to order Ascending/Descending

Code:

#!/bin/bash
Dir="${1%/}"

[[ $Dir == test ]] \
    && mkdir -p testing/inc{1..3} \
    && Dir="testing/inc"

Dirs=() # initial array ${#Dirs[@]} == 0
for i in ${Dir}*;do
    [[ ${i} =~ ${Dir}[0-9]+ ]] && Dirs+=("${i}")
done

Ascending(){
    choose "${Dirs[@]}"
return $?
}
Descending(){
    choose $(
      for (( i=$(( ${#Dirs[@]} - 1 )) ; i >= 0; i-- ));do
          printf "%s " "${Dirs[i]}"
      done
      )
return $?
}
choose(){
    select CHOICE in "${@}";do
        [[ "$CHOICE" ]] && break
    done
    restore "${CHOICE}"
return $?
}
restore(){
# restore function
echo restoring "${1}/metadata"
}

select order in Ascending Descending;do
    [[ "$order" ]] && $order && break
done


bmxakias 09-02-2019 09:45 AM

Can you please remove from your code the creation of the folders (as i do that from the backup script) and i already adjust it and now i have:

Code:

/backup/$DOMAIN/$DATE/database//inc/inc001
/backup/$DOMAIN/$DATE/database/inc/inc002
/backup/$DOMAIN/$DATE/database/inc/inc003


Also Ascending/Descending doesn't matter .... we may remove that also....

I just need to list folders and get the user input ...

With the above code i am getting the Ascending/Descending menu but i don't see anything listed to select and see if it works...

Thank you

BW-userx 09-02-2019 09:50 AM

N/M

Firerat 09-02-2019 10:01 AM

:D

bless

I was just trying to inspire , not actually write it for you

look at them as working examples, not working scripts.


out of interest, how did you pad with 00?

bmxakias 09-02-2019 10:10 AM

Code:

while [[ -d "$INCDIR/inc$N" ]] ; do
    N=$(($N+1))
done

if ls -A $TARGETDIR/* > /dev/null 2>&1; then
    mkdir -p "$INCDIR/inc00$N"
    MYDIR="$INCDIR/inc00$N"
    touch $MYDIR/file.txt
fi

Another thought is to ask for a user input by folder name like:

Which inc folder you want to use bla bla bla but again if the user type inc001 or inc004 i must find a way to identify that and run for inc001 one command and for inc004 four commands for each folder inc001, inc002, inc003 and inc004....

Don't know....

BW-userx 09-02-2019 10:15 AM

Quote:

Originally Posted by bmxakias (Post 6032342)
Code:

while [[ -d "$INCDIR/inc$N" ]] ; do
    N=$(($N+1))
done

if ls -A $TARGETDIR/* > /dev/null 2>&1; then
    mkdir -p "$INCDIR/inc00$N"
    MYDIR="$INCDIR/inc00$N"
    touch $MYDIR/file.txt
fi


replace
Code:

  N=$(($N+1))
with
((N++))


Firerat 09-02-2019 10:17 AM

what happens whey you get to 10?

BW-userx 09-02-2019 10:22 AM

exactly I was just thinking the 'same thing'. when you while loop finds last dir it stops incrementing at that number, then your next operation is to do something with a link and a number attached to it using $N, but mkdir *$N = last number found. ending with dir already present message.

or being that between you check without leading 00 and mkdir having leading 00. you know what you're doing over what I am speculating.

but Firerat might be referring to
009
0010
apposed to
009
010

Firerat 09-02-2019 10:34 AM

Quote:

Originally Posted by BW-userx (Post 6032347)
but Firerat might be referring to
009
0010
apposed to
009
010

bingo

edit, well
001
0010
0011
002
009

kinda messed up if you want order

bmxakias 09-02-2019 10:35 AM

After testing the code with 001 and ((N++)) the only working with no issues and up to 20 that i tested with no issues is this code:

Code:

while [[ -d "$INCDIR/inc$N" ]] ; do
    N=$((N++))
done

if ls -A $TARGETDIR/* > /dev/null 2>&1; then
    mkdir -p "$INCDIR/inc$N"
    MYDIR="$INCDIR/inc$N"
    touch $MYDIR/file.txt
fi

It creates the inc1 inc2 inc10 inc15 folders...

Now i am thinking to find a way to compare the input like:

check if user selected inc1 and if yes do this. If user selected inc2 do that and so on.... As there will not be more than 23 folders....


So maybe you can help me on that?

How to compare user input (use read to get it) with inc1 inc2 and so on ?

Thank you !

BW-userx 09-02-2019 10:45 AM

Quote:

Originally Posted by Firerat (Post 6032354)
bingo

edit, well
001
0010
0011
002
009

kinda messed up if you want order

modified from something I found online
Code:

for i in `seq -w 1 100 | sed -n '1,100p'`;
do
    echo $i;
done
001
002
003
004
005
006
007
008
009
010
011
012
....
100


Firerat 09-02-2019 10:51 AM

Code:

mkdir -p "foo/bar/inc$( printf "%03d" ${N} )"
#edit I missed the 0 in %03d



Quote:

Originally Posted by bmxakias (Post 6032331)
Can you please remove from your code the creation of the folders (as i do that from the backup script) and i already adjust it and now i have:

Code:

/backup/$DOMAIN/$DATE/database//inc/inc001
/backup/$DOMAIN/$DATE/database/inc/inc002
/backup/$DOMAIN/$DATE/database/inc/inc003


Also Ascending/Descending doesn't matter .... we may remove that also....

I just need to list folders and get the user input ...

With the above code i am getting the Ascending/Descending menu but i don't see anything listed to select and see if it works...

Thank you

did you pick 1 or 2?
you can skip that in the code

reverse order?

Code:

#select order in Ascending Descending;do
#    [[ "$order" ]] && $order && break
#done

Descending

# see what I did?

have you tried the one in #8
https://www.linuxquestions.org/quest...8/#post6032085

bmxakias 09-02-2019 10:51 AM

I think that will work:

Code:

echo -e "Please type your inc folder name: \c"
read inc
if [[ $inc = "inc1" ]]
then
    echo good
else
    echo not good
fi

repeat that up to 23 ....

But how can i do that to avoid repeating this code 23 times? :)

BW-userx 09-02-2019 10:51 AM

Quote:

Originally Posted by bmxakias (Post 6032356)
After testing the code with 001 and ((N++)) the only working with no issues and up to 20 that i tested with no issues is this code:

Code:

while [[ -d "$INCDIR/inc$N" ]] ; do
    N=$((N++))
done

if ls -A $TARGETDIR/* > /dev/null 2>&1; then
    mkdir -p "$INCDIR/inc$N"
    MYDIR="$INCDIR/inc$N"
    touch $MYDIR/file.txt
fi

It creates the inc1 inc2 inc10 inc15 folders...

Now i am thinking to find a way to compare the input like:

check if user selected inc1 and if yes do this. If user selected inc2 do that and so on.... As there will not be more than 23 folders....


So maybe you can help me on that?

How to compare user input (use read to get it) with inc1 inc2 and so on ?

Thank you !

If I go for solution, there will be two completely different methodologies being used here. It looks like you've taken to internal usage of VARs in here.

you can run script.
script gets list of dirs already created. puts them into an array, prints out results, ask user what dir he wants to back up ( I think that is whats going on here.) then using case statement find match then issue commands to back it up.

if you use functions you can incorporate flags to call the functions so your entire created and back up can be in the same script.

bmxakias 09-02-2019 10:57 AM

Just edited my message above:


I think that will work also:

Code:

echo -e "Please type your inc folder name: \c"
read inc
if [[ $inc = "inc1" ]]
then
    echo good
else
    echo not good
fi

repeat that up to 23 ....

But how can i do that to avoid repeating this code 23 times? :)

What do you think about that?

Thank you

BW-userx 09-02-2019 11:01 AM

find /backupFolders > makealist
print list
ask user what number he wants
take that and go from there

Firerat 09-02-2019 11:10 AM

yeap


Regards the restore
what happens if one of the incremental backups is missing?

how are these inc. backups being made?

do you really need to restore 1 and 2 to get 3?

i.e., when you create backup inc1
that is a copy of original...

inc2 that is the difference between inc1 and the Current
inc3 that is the difference between inc1+inc2 and the Current

That is how I understand it based on the required restore inc1 then inc2 then inc3

Or, is each one a snapshot in time?


the former is more space efficient but more complicated, and risk of failure increases each backup ( since the last one depends on all the ones before it )

the later is far easier, but uses more space

BW-userx 09-02-2019 11:15 AM

this is just an working example of known var / dir names
Code:

baseDir=$HOME/test
makeDir()
{
for i in `seq -w 1 100 | sed -n '1,100p'`;
do
    mkdir -p $baseDir/inc$i
done
}
#touch $baseDir/dirlist
findDirs()
{
        find $baseDir -type d >> $baseDir/dirlist       
}

countDir()
{
        count=$(find $baseDir -type d -mindepth 1 -maxdepth 2 | wc -l)
}

#makeDir
findDirs
countDir
cat $baseDir/dirlist
echo              #removes leading space
echo select inc001 - inc${count// }

creates 100 dir inc001 - inc100
gets count then using the known format shows amount up to last dir created to its 100 but removing the cat and only using the echo you'll get a sequence of start to last to pick from.

select inc001 - inc100

Firerat 09-02-2019 11:38 AM

Quote:

Originally Posted by BW-userx (Post 6032376)
this is just an working example of known var / dir names

doesn't do the creation ( did that in other thread )


Code:

#!/bin/bash
Dir="${1%/}"
Dirs=() # intial array ${#Dirs[@]} == 0
for i in ${Dir}*;do
    [[ ${i} =~ ${Dir}[0-9]+ ]] && Dirs+=("${i}")
    ## checks if basename<numbers>
done


echo "Directory count = ${#Dir[@]}" # you want a count?
# no need to write files to disk ( which you need to clean up )
# no using find, bash globbed them
#

select CHOICE in "${Dirs[@]}";do
    [[ "$CHOICE" ]] && break
done

for ((i=0;i <= $(( $REPLY - 1 ));i++));do
        echo restore "${Dirs[i]}"
done

simple
and no clean up

BW-userx 09-02-2019 11:42 AM

Quote:

Originally Posted by Firerat (Post 6032386)
doesn't do the creation ( did that in other thread )


Code:

#!/bin/bash
Dir="${1%/}"
Dirs=() # intial array ${#Dirs[@]} == 0
for i in ${Dir}*;do
    [[ ${i} =~ ${Dir}[0-9]+ ]] && Dirs+=("${i}")
    ## checks if basename<numbers>
done


echo "Directory count = ${#Dir[@]}" # you want a count?
# no need to write files to disk ( which you need to clean up )
# no using find, bash globbed them
#

select CHOICE in "${Dirs[@]}";do
    [[ "$CHOICE" ]] && break
done

for ((i=0;i <= $(( $REPLY - 1 ));i++));do
        echo restore "${Dirs[i]}"
done

simple
and no clean up

so just disregard that part of it, and take the relevant part. The point I was showing is get the total amount already present then offer that to the user to remove the guess work.

Firerat 09-02-2019 11:59 AM

basic dir creation

Code:

#!/bin/bash
Dir="${1%/}"
Dirs=()
for i in ${Dir}*;do
        [[ ${i} =~ ${Dir}[0-9]+ ]] && Dirs+=("${i}")
done

mkdir -vp "${Dir}$( printf "%03d" $(( ${#Dirs[@]} + 1 )) )"

where is fails is when there is a gap ( or gaps )
1, 2, 4, 5
recreates 5

we could check the array with something like this

Code:

foo=( "inc001" "inc002" "inc004" )
for ((i=0;i<${#foo[@]};i++));do
    [[ inc$( printf "%03d" $((i+1)) ) == ${foo[i]} ]] \
      && echo ${foo[i]} is good \
      || echo ${foo[i]} is bad
done



All times are GMT -5. The time now is 08:19 AM.