1

So we have this script that is supposed to change the IP of a linux machine based on user input. This user input has to be validated.

If the script is run inside the directory in which it lays, everything works as expected, but as soon as it's run with an absolute path, it seems to break on some points.

I already tried to use the debug option set -x but the output stays almost the same.

read -p "Please enter the netmask (CIDR format): " netmask

if [ ! $(echo "$netmask" | egrep  "^([12]?[0-9]?)$") ];
then
     subnetok=0
fi

if [ "$subnetok" == "0" ];
then
     echo -e "\033[41m\033[39m Subnetmask is invalid!\033[0m"

     sleep 1
     return 1
fi

This is the debug output if the script is run inside the directory:

++ echo 24
++ egrep '^([12]?[0-9]?)$'
+ '[' '!' 24 ']'
+ '[' '' == 0 ']'

and this is the debug output if the script is run with an absolute path

+++ echo 24
+++ egrep --color=auto '^([12]?[0-9]?)$'
++ '[' '!' 24 ']'
++ '[' 0 == 0 ']'
++ echo -e 'Subnetmask is invalid'

I expect the output to be the same with the same numbers

Laurent
  • 39
  • 1
  • 5
  • What do you mean by "running with absolute path"? Post how you invoked the scripts. Did you make sure that you were running the *identical* script in both cases? – user1934428 Jul 23 '19 at 06:57
  • @user1934428 yes it is the same script. By running with an absolute path I mean . /usr/local/script/script.sh instead of cd into /usr/local/script/ and then ./script.sh – Laurent Jul 23 '19 at 07:08
  • Looks like in one case the egrep called is an alias set by user shell. How about you use `/bin/egrep/` instead of `egrep`. Also, are you sure that the script is run by `/bin/sh`? – January Jul 23 '19 at 07:22
  • 3
    I'd suggest instead of `if [ ! $(echo | egrep) ];` you use `if ! echo "$netmask" | grep -qE '^([12]?[0-9]?)$';` egrep and fgrep are deprecated, and the [-q option](https://www.gnu.org/savannah-checkouts/gnu/grep/manual/grep.html#General-Output-Control) is more suitable for if statements. – Dabombber Jul 23 '19 at 07:29
  • @January I tried using /bin/egrep but I still got the same issue. My script is run by /usr/bin/env bash – Laurent Jul 23 '19 at 07:32
  • @Dabombber thanks for the information! But this still doesn't seem to change the situation, still got the same behaviour – Laurent Jul 23 '19 at 07:35
  • 2
    Put into the question the command lines you run to get different results. – axiac Jul 23 '19 at 08:03
  • @January : Can't be, because aliases can't be exported, and there is no way the script would see an alias definition in the parent shell. Also, the actual shell (sh vs. something else) does not matter here, because the OP does not explicitly supply a shell and hence the shell must be the same. – user1934428 Jul 23 '19 at 09:36
  • It can if you run the script with`.`. See the answer below. – January Jul 23 '19 at 09:38
  • @January : True, but he did not run it with `.`. See his comment: He explicitly says how he executed it. Even if he sourced the script, sourcing a script with absolute path would expose the aliases in the same way. – user1934428 Jul 23 '19 at 09:40
  • He only stated that `#!/bin/env bash` was the first line. But yeah, you are right, and I am right, so there ;-) – January Jul 23 '19 at 09:42
  • @LaurentHee : The only difference hence is the working directory, but you don't have any dependency of the working directory, expect in the bizarre case that your PATH contains `.` **and** your working directory has it's own, executable named `[`. – user1934428 Jul 23 '19 at 09:42
  • @January : Further, we see that `egrep` can't be the culprit, because the trace shows that in both cases, subnetok is set to 24. – user1934428 Jul 23 '19 at 09:43
  • @LaurentHee : For the safe side, do the following two diagnostics: (1) If you have `realpath` installed, do a `realpath $0` at the start of your script, if you haven't, do a `echo $PWD; echo $0` instead. (2) Before entering the second `if`, do a `print $subnetok|od -cx`. – user1934428 Jul 23 '19 at 09:48

2 Answers2

3

When you run the script with this:

. /usr/local/script/script.sh 

This uses the . command, which runs the script in the current shell (equivalent to source). That is, it runs it in your interactive shell rather than forking a new shell to run it. See: What is the difference between ./somescript.sh and . ./somescript.sh

This has (at least) two effects:

  • The current shell is interactive, and apparently has an alias defined for egrep, which makes things a little weird. Not really a problem, just weird.

  • The current shell apparently already has a definition for the variable subnetok, and it's "0". It's probably left over from a previous time you ran the script this way. This is what's causing the problem.

The primary solution is that the script needs to explicitly initialize subnetok rather than just assuming that it's undefined:

subnetok=1
if ...

Alternately, if you don't need the variable for anything else, you could just skip it and handle the condition immediately:

if [ ! $(echo "$netmask" | egrep  "^([12]?[0-9]?)$") ];    # See below for alternatives
then
    echo -e "\033[41m\033[39m Subnetmask is invalid!\033[0m"
    ...

Other recommendations:

  • Run the script without the .:

    /usr/local/script/script.sh
    
  • Give the script a proper shebang line (if it doesn't already have one) that specifies the bash shell (i.e. #!/bin/bash or #!/usr/bin/env bash).

  • Use a better method to check the subnet for validity, like:

    if ! echo "$netmask" | grep -Eq  "^[12]?[0-9]?$"
    

    or (bash only):

    if ! [[ "$netmask" =~ ^[12]?[0-9]?$ ]]
    
  • Don't use echo -e, as it's not portable (even between different versions of the same OS, modes of the shell, etc). Use printf instead (and I'd recommend single-quotes for strings that contain backslashes, because in some cases they'll get pre-parsed when in double-quotes):

    printf '\033[41m\033[39m Subnetmask is invalid!\033[0m'
    

    Note that printf is not a drop-in replacement for echo -e, it's considerably more complicated when you're using variables and/or multiple arguments. Read the man page.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
1

Comment:

By running with an absolute path I mean . /usr/local/script/script.sh instead of cd into /usr/local/script/ and then ./script.sh

The difference is that in one case you are executing the script and in another case you are sourcing the script. See What is the difference between executing a Bash script vs sourcing it? for more information.

When you are running ./script.sh without a space between the dot and the slash you are executing the script in a new shell. When you are running . /usr/local/script/script.sh you are sourcing the script in the current shell. This can have implications if you have for example an alias set in your current shell that would not be present in a new shell, such as alias egrep='egrep --color=auto'. That's why there is a difference.

From the linked question:

Both sourcing and executing the script will run the commands in the script line by line, as if you typed those commands by hand line by line.

The differences are:

  • When you execute the script you are opening a new shell, type the commands in the new shell, copy the output back to your current shell, then close the new shell. Any changes to environment will take effect only in the new shell and will be lost once the new shell is closed.

  • When you source the script you are typing the commands in your current shell. Any changes to the environment will take effect and stay in your current shell.

Use source if you want the script to change the environment in your currently running shell. use execute otherwise.

Community
  • 1
  • 1
Secespitus
  • 710
  • 2
  • 14
  • 22
  • Thanks for the answer, I didn't know that! I removed the alias but that doesn't seem to resolve the problem. What else could I do to have the same environment in both cases? – Laurent Jul 23 '19 at 07:54