Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
bash script command line argumens iteration using a index
View unanswered posts
View posts from last 24 hours

 
Reply to topic    Gentoo Forums Forum Index Unsupported Software
View previous topic :: View next topic  
Author Message
CaptainBlood
Veteran
Veteran


Joined: 24 Jan 2010
Posts: 1596

PostPosted: Sun Apr 12, 2020 2:14 pm    Post subject: bash script command line argumens iteration using a index Reply with quote

I'm trying to process all beyond position 0 command line arguments.
I know there are other methods but I'm trying to use 'eval' to dynamically evaluate $1, $2 as $n through a variable.
Code:
#!/bin/bash
for((i=1;i<=$#;i++))
do

    eval echo cmd line parameter found at position $i has a value of "\'"\$$i"\'"
    eval var=\$$i

    case $var in
        expected)
            echo cmd line parameter at position $i has an expected value
            ;;
        *)
            echo cmd line parameter at position $i has an unexpected value
    esac

    # using eval let's try to avoid use of the 'var' variable defined above
   # FAILS !!!
    eval case \$$i in
        expected)
            echo cmd line parameter at position $i has an expected value
            ;;
        *)
            echo cmd line parameter at position $i has an unexpected value
    esac
done
Any idea?
Thks 4 ur attention, interest & support.


Last edited by CaptainBlood on Sun Apr 12, 2020 11:29 pm; edited 4 times in total
Back to top
View user's profile Send private message
Ionen
l33t
l33t


Joined: 06 Dec 2018
Posts: 698

PostPosted: Sun Apr 12, 2020 2:40 pm    Post subject: Reply with quote

eval combines arguments into a single string and evaluates it, so you can just go ahead and in fact pass it a single string
Code:
name=testvar
testvar=value
eval "
  case \$$name in
    value) echo valued match;;
  esac
"
Just the first "case \$$i in" is an invalid statement on its own, it doesn't do in-line replacement, it executes it.
Back to top
View user's profile Send private message
CaptainBlood
Veteran
Veteran


Joined: 24 Jan 2010
Posts: 1596

PostPosted: Sun Apr 12, 2020 2:52 pm    Post subject: Reply with quote

OMG, blazing fast :twisted: :lol:
So much leaner sample script! 8O :oops:
Now trying... done! :wink: 8)
Thks 4 ur attention, interest & support
Back to top
View user's profile Send private message
Hu
Moderator
Moderator


Joined: 06 Mar 2007
Posts: 15334

PostPosted: Sun Apr 12, 2020 4:49 pm    Post subject: Reply with quote

Using eval is usually a sign of a bad design elsewhere, and will get flagged in most code reviews because of the difficulty of using eval safely. Could you explain why you want to use this instead of other more straightforward solutions? For what you showed, you could iterate through the arguments keeping a counter.
Back to top
View user's profile Send private message
Zucca
Veteran
Veteran


Joined: 14 Jun 2007
Posts: 1778
Location: KUUSANKOSKI, Finland

PostPosted: Sun Apr 12, 2020 7:48 pm    Post subject: Reply with quote

What Hu said +
Code:
while [[ $1 ]]
do
    case "$1" in
        <...>
    esac

shift
done
... is an easy way of going trough the arguments.
"shift" drops the first argument and... shifts all the others by one posision backwards, or rather minuses one from their position.

I usually use
Code:
while [[ "${1:0:1}" = "-" ]]
to go trough command line switches.
${1:0:1} is the first character of the (current) first argument. And on the above it checks if it's a dash (-).
_________________
..: Zucca :..

Code:
ERROR: '--failure' is not an option. Aborting...
Back to top
View user's profile Send private message
CaptainBlood
Veteran
Veteran


Joined: 24 Jan 2010
Posts: 1596

PostPosted: Mon Apr 13, 2020 1:25 am    Post subject: Reply with quote

me wrote:
I'm trying to process all beyond position 0 command line arguments.
I know there are other methods but I'm trying to use the infamous heterodox well worthing a rap on the knuckles 'eval' instruction to dynamically evaluate $1, $2 as $n through a variable. well knowing about the classic implement using the 'shift' instruction
would have been more accuratly elaborated. My apologies.:oops:

@Hu: Thks for reformulating what I'm functionally trying to achieve, so that I can update the title of this post accordingly.
wrote:
Using evalthe difficulty of using eval safely.
Maybe you have pointers to share on what such safe use cases should explicitly be?
ATEOTD this instruction must exist for a reason, good plausibly...:roll:
wrote:
you could iterate through the arguments keeping a counter.
Could please elaborate, as I feel illiterate in this respect.:(

@Zucca: Thks for your interesting use case proposal, but 'shift' is just what I wish to avoid!
It would have be fine if 'shift' worked like a ring buffer, pushing $0 at the end, i.e. the right, of the command line argument array.
Admittingly better than bad by design, it regrettably destroys information instead still, e.g. heavier to manage multiple command line argument parsing stages.
That said, this solution remains as plan B.:)

Thks 4 ur attention, interest & support.
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 18439

PostPosted: Mon Apr 13, 2020 2:36 am    Post subject: Reply with quote

Maybe if you explained what you're trying to accomplish? Why avoid shift, and what do you want to achieve with the "ring buffer"? Are you trying to preserve $0?

Also, I just learned about PARAM.

Code:
$ ./foo bar -a -b --three
# Arguments: 4
bar
-a
-b
--three
$ cat foo
#!/bin/sh

echo "# Arguments: ${#}"

for PARAM; do
    echo $PARAM
done

_________________
The media sells it and you live the role.
Back to top
View user's profile Send private message
Hu
Moderator
Moderator


Joined: 06 Mar 2007
Posts: 15334

PostPosted: Mon Apr 13, 2020 3:38 am    Post subject: Reply with quote

CaptainBlood wrote:
Maybe you have pointers to share on what such safe use cases should explicitly be?
Historically, your attempt at indirect variable assignment might be done through an eval, after very careful sanitization of the input parameter. With modern bash, it is often (though perhaps not always) possible to avoid eval. For many cases, nameref variables can be used instead. However, I could not get a nameref variable to alias a numbered value, so I did not suggest that above. For some use cases, printf -v varname can be a good solution, since it assigns to the variable instead of printing.

In my opinion, the main reason eval exists is because it is easy to provide, and is done in the hope that providing it avoids users creating even more complicated and unsafe constructs, such as forking a subshell and sending the to-be-eval'd text to the subshell's stdin.

As an example of using a counter:
Code:
#!/bin/bash

# Load some dummy parameters for demonstration
set -- a b c

# Set the counter.  It would be nice if this could be handled
# automatically like Python's enumerate, but this will do.
i=0
for p; do
   # Increment the counter.  Do this here since $0 is not part of the
   # 'for' iteration, so the first match should be $1.
   (( ++ i ))
   case "$p" in
      a)
         echo "found a as $i"
         ;;
      b)
         echo "found b as $i"
         ;;
   esac
done

If you need to skip around in the list, you can use slicing on the argument array:
Code:
#!/bin/bash

# Load some dummy parameters for demonstration
set -- a b c

for ((i=1; i <= $#; ++i)); do
   p="${@: $i:1}"
   echo "\$$i = $p"
   case "$p" in
      a)
         echo "found a as $i"
         ;;
      b)
         echo "found b as $i"
         ;;
   esac
done
Back to top
View user's profile Send private message
pa4wdh
Guru
Guru


Joined: 16 Dec 2005
Posts: 363

PostPosted: Mon Apr 13, 2020 8:28 am    Post subject: Reply with quote

Why not use getopt, a function made to parse commandline arguments?
It's really easy, some examples can be found here: https://www.computerhope.com/unix/bash/getopts.htm
_________________
The gentoo way of bringing peace to the world:
USE="-war" emerge --newuse @world

Free as in Freedom is not limited to software only:
Music: http://www.jamendo.com
Recipes: http://www.opensourcefood.com
Back to top
View user's profile Send private message
mv
Watchman
Watchman


Joined: 20 Apr 2005
Posts: 6345

PostPosted: Mon Apr 13, 2020 9:20 am    Post subject: Reply with quote

pjp wrote:
Also, I just learned about PARAM.

It seems that there is a misunderstanding: PARAM is not any special name, it is just any variable name. It is just that
Code:
for var
do ...
done

is equivalent to
Code:
for var in "$@"
do ...
done
Back to top
View user's profile Send private message
fedeliallalinea
Bodhisattva
Bodhisattva


Joined: 08 Mar 2003
Posts: 24078
Location: here

PostPosted: Mon Apr 13, 2020 9:29 am    Post subject: Reply with quote

The problem of getopts is that not accept long name, for workaround this is better use getopt
Code:
#!/bin/bash

OPTIONS=$(getopt -o p:h --long pet:,help -- "$@")
if [ $? != 0 ] ; then exit 1 ; fi

$(set -- "${OPTIONS}")
while [ "${1}" != "--" ]; do
   case "${1}" in
      -p|--pet)
         shift; # The arg is next in position args
         echo "The pet is ${1}"
         ;;
      -h|--help)
         echo "print help"
         ;;
      * ) break ;;
   esac
   shift
done

_________________
Questions are guaranteed in life; Answers aren't.
Back to top
View user's profile Send private message
krinn
Watchman
Watchman


Joined: 02 May 2003
Posts: 7416

PostPosted: Mon Apr 13, 2020 12:12 pm    Post subject: Reply with quote

CaptainBlood wrote:

wrote:
Using evalthe difficulty of using eval safely.
Maybe you have pointers to share on what such safe use cases should explicitly be?

he mean you should just never ever use eval to assign a variable with an argument you doesn't have the control of its value without validate that value first

it is a bad idea to eval var=\$$i
run your script with \"\"; date as command line you'll see (if you wonder it mean you will end with eval var=""; date)

that's what Hu mean when saying
Hu wrote:
your attempt at indirect variable assignment might be done through an eval, after very careful sanitization of the input parameter
Back to top
View user's profile Send private message
szatox
Veteran
Veteran


Joined: 27 Aug 2013
Posts: 1879

PostPosted: Mon Apr 13, 2020 2:00 pm    Post subject: Reply with quote

I love little challenges, so I thought I'd join the fun:

Code:
 $ fun () { a=("$@")
for i in $(seq 0 "$((${#a[@]}-1))")
do echo "$i: ${a[$i]}"
done
}
 $ fun a b c $(seq 1 15) "d e f" g
0: a
1: b
2: c
3: 1
4: 2
5: 3
6: 4
7: 5
8: 6
9: 7
10: 8
11: 9
12: 10
13: 11
14: 12
15: 13
16: 14
17: 15
18: d e f
19: g


So.... I guess it works. I do not recommend using it though.
There are better ways to do that. Like actually consuming those parameters and e.g. putting their values in named variables you can use in your code directly, without need of tracking indices for this particular invocation. (What if you provide parameters in different order next time?)
Code:
while [[ "${#@}" -gt 0 ]]
do something_about "$1"
shift
done
Back to top
View user's profile Send private message
pjp
Administrator
Administrator


Joined: 16 Apr 2002
Posts: 18439

PostPosted: Mon Apr 13, 2020 4:23 pm    Post subject: Reply with quote

mv wrote:
pjp wrote:
Also, I just learned about PARAM.

It seems that there is a misunderstanding: PARAM is not any special name, it is just any variable name. It is just that
Code:
for var
do ...
done

is equivalent to
Code:
for var in "$@"
do ...
done
Thanks for clarifying. I don't recall seeing that synatx, or at least not perceiving it.
_________________
The media sells it and you live the role.
Back to top
View user's profile Send private message
mv
Watchman
Watchman


Joined: 20 Apr 2005
Posts: 6345

PostPosted: Mon Apr 13, 2020 5:51 pm    Post subject: Reply with quote

krinn wrote:
it is a bad idea to eval var=\$$i

No, if the content of the variable i is a number (and your script controls that) this is completely fine. Only you should do
Code:
eval var=\${$i}

to deal with numbers >9.
Back to top
View user's profile Send private message
Zucca
Veteran
Veteran


Joined: 14 Jun 2007
Posts: 1778
Location: KUUSANKOSKI, Finland

PostPosted: Mon Apr 13, 2020 6:38 pm    Post subject: Reply with quote

CaptainBlood wrote:
@Zucca: Thks for your interesting use case proposal, but 'shift' is just what I wish to avoid!
It would have be fine if 'shift' worked like a ring buffer, pushing $0 at the end, i.e. the right, of the command line argument array.
I'm not sure what you're after but you could transform all the command line arguments into an array:
Code:
cliargs=("$@")
Now you can go trough "cliargs" using for loop. Similar to what szatox showed.
Code:
for((i=0;i<=${cliargs#};i++))
do
    echo "${cliargs[$i]}"
done

_________________
..: Zucca :..

Code:
ERROR: '--failure' is not an option. Aborting...
Back to top
View user's profile Send private message
krinn
Watchman
Watchman


Joined: 02 May 2003
Posts: 7416

PostPosted: Tue Apr 14, 2020 2:43 pm    Post subject: Reply with quote

mv wrote:
krinn wrote:
it is a bad idea to eval var=\$$i

No, if the content of the variable i is a number (and your script controls that) this is completely fine. Only you should do
Code:
eval var=\${$i}

to deal with numbers >9.

so you are validating the value first...
i don't get your point there?
Back to top
View user's profile Send private message
Zucca
Veteran
Veteran


Joined: 14 Jun 2007
Posts: 1778
Location: KUUSANKOSKI, Finland

PostPosted: Tue Apr 14, 2020 3:32 pm    Post subject: Reply with quote

krinn wrote:
i don't get your point there?
I think that if you validate that i is a valid number (match extended regexp of ^[0-9]+$ for example) you can safely do
Code:
eval var=\${$i}
... it doesn't still mean that you should, because there are other "more correct" ways to do it, but after validation of i that code is safe.
_________________
..: Zucca :..

Code:
ERROR: '--failure' is not an option. Aborting...
Back to top
View user's profile Send private message
krinn
Watchman
Watchman


Joined: 02 May 2003
Posts: 7416

PostPosted: Wed Apr 15, 2020 2:52 pm    Post subject: Reply with quote

Zucca wrote:
I think that if you validate that i is a valid number (match extended regexp of ^[0-9]+$ for example) you can safely do
Code:
eval var=\${$i}
...

but i have said it in my first post, that's why i don't get what he means
Back to top
View user's profile Send private message
guitou
Guru
Guru


Joined: 02 Oct 2003
Posts: 451
Location: France

PostPosted: Thu Apr 16, 2020 12:01 pm    Post subject: Reply with quote

Even if I do not manage to see the point, I guess one solution to keep using shift without touching positional parameters would be to parse script arguments in a function, no ?..

++
Gi)
Back to top
View user's profile Send private message
mv
Watchman
Watchman


Joined: 20 Apr 2005
Posts: 6345

PostPosted: Thu Apr 16, 2020 7:46 pm    Post subject: Reply with quote

krinn wrote:
mv wrote:
krinn wrote:
it is a bad idea to eval var=\$$i

No, if the content of the variable i is a number (and your script controls that) this is completely fine. Only you should do
Code:
eval var=\${$i}

to deal with numbers >9.

so you are validating the value first...
i don't get your point there?

The point is that i is not an untrusted variable in the given script. No need to validate trusted variables. Only if you would assign i some user input, you might get a problem, but this is not happening in the script.
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Unsupported Software All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum