1

Actually, I am doing some assignment by follow:

1: Write a program called valid that prints "yes" if its argument is a valid shell variable name and "no" otherwise:

What I am doing is I want to find some value which includes some regular expression, such as [0-9]* by using 'grep' command. But I have no idea how to grab some value including that expression from the argument I input, since 'grep' command is basically to capture some line in the file. Any help will be really appreciated

glenn jackman
  • 84,176
  • 15
  • 116
  • 168
GideokSeong
  • 67
  • 2
  • 8
  • 1
    Do you think they’re expecting a bash script from you? And that the valid variable name is for the bash shell? How much have you learned this far, besides grep? Anything about referring to script parap? – Jeff Schaller May 30 '18 at 20:17
  • Yes, I need to code for a bash script. When script name is 'valid', and I run it as follow: valid 1234 -> it has to print yes or no depending on argument is correct name of variable – GideokSeong May 30 '18 at 20:23
  • It seems like the piece you're missing is how to get the argument. In bash, they are known as the "positional parameters", $1, $2, etc – glenn jackman May 30 '18 at 20:32
  • 4 answers and still no grep! – Jeff Schaller May 30 '18 at 21:57

4 Answers4

3

This doesn't use grep, but as a point of reference, you could use bash's =~ conditional operator to compare the script's first argument with the regular expression class for a name, which is defined by the Bash Reference Manual as:

A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names.

$ cat isvarname
#!/bin/bash
if [ "$#" -ne 1 ]
then
   echo "Usage: $0 a-string"
   exit 1
fi

if [[ "$1" =~ ^[[:alpha:]_][[:alnum:]_]*$ ]]
then
  echo yes
else
  echo no
fi
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
  • Thank you very much and it was very helpful. By the way, how would you know that regular expression? such as =~ ^[[:alpha:]_][[:alnum:]_]*$ ]] – GideokSeong May 30 '18 at 20:33
  • Please don't rush to accept this answer, particularly since I avoided using grep! =~ accepts regular expressions, so I just did it in-shell. To learn more, start with [conditional expressions](https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs) and `man -s7 regex` for a list of character classes. See the [tag:regular-expression] tag on this site for more, such as https://unix.stackexchange.com/questions/119905/why-does-my-regular-expression-work-in-x-but-not-in-y – Jeff Schaller May 30 '18 at 20:37
  • 1
    Can use extended pattern instead of regex: `[[ $1 == [[:alpha:]_]*([[:alnum:]_]) ]]` – glenn jackman May 30 '18 at 20:40
1

In bash a valid variable name is made of one or more single-byte characters with the first one being alphabetical or underscore, and the remaining ones if any being alphabetical, 0123456789 or underscore.

For instance Stéphane is a valid variable name only in locales where é is single-byte like in ISO-8859-1 where it's the 0xE9 byte, not in UTF-8 where it's encoded as 0xC3 0xA9.

You could do something like:

#! /usr/bin/env bash
is_single_byte() {
  local length_in_bytes length_in_chars
  length_in_chars=${#1}
  local LC_ALL=C
  length_in_bytes=${#1}
  ((length_in_bytes == length_in_chars))
}

re='^[[:alpha:]_][[:alnum:]_]*$'
for var do
  if is_single_byte "$var" && [[ $var =~ $re ]]; then
    printf '"%s" is a valid variable name\n' "$var"
  else
    printf '"%s" is not a valid variable name\n' "$var"
  fi
done
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
0
#!/bin/bash
var=$1
if [[ $var =~ [\`\$\=\/] || $var =~ \[ ]]; then
    echo 'no'
    exit
fi
declare "$var"=test 2>/dev/null
if [[ ${!var} == 'test' || $1 == 'var' ]]; then
    echo 'yes'
else
    echo 'no'
fi

This will try to assign the value test to the provided value. If it succeeds (if the value is a valid variable name) it will match the if test and echo yes, else it will echo no.

jesse_b
  • 35,934
  • 12
  • 91
  • 140
  • 3
    Generally a bad idea to use eval: `bash ./your_script '(echo rm /very/important/files); foo'` -- by the time the syntax error occurs, your files are already deleted. – glenn jackman May 30 '18 at 20:31
  • `./script '/usr/sbin/shutdown -h; x'` :) – Jeff Schaller May 30 '18 at 20:33
  • @StéphaneChazelas: Updated again, however I cannot get it to accept `a[1]` either way. – jesse_b May 30 '18 at 22:25
  • @StéphaneChazelas: Quoted `$var` to prevent globbing. Also I would argue that it's a good thing to tell students `RANDOM`, `EUID`, and `UID` are not valid variable names. :p – jesse_b May 30 '18 at 22:40
  • Modified again. – jesse_b May 30 '18 at 22:47
  • That's better now. So I'll clean-up the comments. Remaining issues: it still outputs some error messages without outputting no for most invalid variables (`+`, `é`, `''`...). If more code is added to that script, calling it with things like `PATH` or `IFS` can have nasty side effects. It would report no for valid variable names that correspond to readonly ones (like UID, BASHOPTS...) or special ones set by bash like `RANDOM`, `OPTIND`. It returns yes for `that-script 2 test`. – Stéphane Chazelas May 31 '18 at 15:29
0

The first command line argument is available as $1. A valid shell variable name starts with a alphabetic character (or underscore) and continues with alphanumerics (or underscores).

Two shell patterns that matches an invalid shell variable name is

[!a-zA-Z_]*

and

[a-zA-Z_]*[!a-zA-Z_0-9]*

You may use case ... esac to do pattern matching against a variable's value.

Spoiler warning:

#!/bin/sh
LC_ALL=C
case "$1" in
    [!a-zA-Z_]*|[a-zA-Z_]*[!a-zA-Z_0-9]*|"")
        echo 'NO'
        ;;
    *)
        echo 'YES'
esac
This also answers "NO" for a variable whose name is empty. Note that this is using shell globbing patterns, not regular expressions, and that it runs in any POSIX shell, not just bash.

Testing:

$ ./script.sh _ae
YES
$ ./script.sh 0a9oe
NO
$ ./script.sh aoeat
YES
$ ./script.sh aoeat-aoe
NO
Kusalananda
  • 320,670
  • 36
  • 633
  • 936