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
  • 85,964
  • 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
  • 67,283
  • 35
  • 116
  • 255
  • 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 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
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
  • 37,005
  • 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
  • 333,661