-3

I want this function to return one random word if there aren't any command-line arguments.

I am modifying linuxconfig.org's random-word generator to run even when #$ -ne 1.

function random-word {
    if [ $# -eq 0 ] ;
    then 
        echo "I only take one argument, dummy"
        # previously was exit 0
    fi

    # Constants 
    X=0
    ALL_NON_RANDOM_WORDS=/usr/share/dict/words

    # total number of non-random words available 
    non_random_words=`cat $ALL_NON_RANDOM_WORDS | wc -l` 

    # while loop to generate random words  
    # number of random generated words depends on supplied argument 
    while [ $X -lt "$1" ] 
    do 
    random_number=`od -N3 -An -i /dev/urandom | 
    awk -v f=0 -v r="$non_random_words" '{printf "%i\n", f + r * $1 / 16777216}'` 
    sed `echo $random_number`"q;d" $ALL_NON_RANDOM_WORDS 
      let "X = X + 1" 
    done

The statement executes, but there's a bash error:

$ bob
I only take one argument, dummy
bash: [: : integer expression expected

How do I fix this so the bash error won't display?

mrgnw
  • 211
  • 3
    The error is further in your script, since it's display after the “dummy” message. – Gilles 'SO- stop being evil' Jul 22 '14 at 23:50
  • @glennjackman The smart quotes are an artefact of the site's CSS. – Gilles 'SO- stop being evil' Jul 22 '14 at 23:54
  • @mrgnw, please show the code that is causing the error – glenn jackman Jul 22 '14 at 23:56
  • Good call - I assumed it wasn't, because this didn't happen before and (I thought) the only section I changed was the if statement. – mrgnw Jul 22 '14 at 23:56
  • @Gilles That was it. I added code which I (incorrectly) assumed wasn't part of the problem.

    while [ $x -lt "$1" ] still executes when there are no parameters. The if block previously contained an exit to prevent this from happening.

    – mrgnw Jul 23 '14 at 00:02
  • @mrgnw So you solved your problem? – Seth Jul 23 '14 at 00:03
  • 3
    Given what you've posted now: the value of x isn't what you think. What is it? I don't know, since you didn't post that part of the code. Stop wasting our time and post a working example. – Gilles 'SO- stop being evil' Jul 23 '14 at 00:03
  • @Gilles I posted the rest of the code. Using your comment, I was able to solve the problem. I'll accept your answer if you submit it (since your comment led me to the answer), or I can post the answer I implemented. – mrgnw Jul 23 '14 at 00:12
  • 2
    The code you posted still isn't complete (it's the start of a function definition) and the fragment here still doesn't exhibit the issue. As it stands, this question is not useful to anybody else, hence it should be closed. Let's stop here, shall we? – Gilles 'SO- stop being evil' Jul 23 '14 at 00:15
  • Yes. I misunderstood the problem. Thank you for your help. – mrgnw Jul 23 '14 at 00:19
  • Why do you only take one argument? There's no sense in that. You might as well just: arg=${1?ERR: I need at least one argument!} set -- at the start of your script. That way you work with one argument, fail if you don't get at least the one, and ignore all others. – mikeserv Jul 23 '14 at 00:20
  • 1
    @Gilles - you're completely wrong here. The most useful questions are the ones that demonstrate what not to do. This is an incredibly useful question. – mikeserv Jul 23 '14 at 00:23
  • 2
    @Gilles - see the answer? it's a great question. and the answer is very good. – mikeserv Jul 23 '14 at 00:25

2 Answers2

4

while [ $X -lt "$1" ] is being evaluated even when there is no $1.

Move the rest of the code into an else block so this doesn't happen.

function random-word {
    # from linuxconfig.org 

    if [ $# -eq 0 ] 
    then 
        echo "I need an argument, dummy"
        # To be extra friendly, give them a random word.
        echo "Here's a random word:"
        random-word 1
    else
        # Constants 
        X=0

         ...

        sed `echo $random_number`"q;d" $ALL_NON_RANDOM_WORDS 
          let "X = X + 1" 
        done
    fi
}
mrgnw
  • 211
2

You don't need an else block. And you don't need to fail if you receive more than one argument. You only need to fail if you don't get an argument and you can then just ignore everything else or quit if you don't get at least the one.

set -- "${1?ERR: Where\'s my argument?!?!}"

That statement does all of that. This does the same but also fails if the first argument is only the '' nullstring :

set -- "${1:?ERR: Where\'s my argument?!?!}"

Of course, if the point is to be a shell function called from an interactive shell, this might kill the interactive shell as well, so you can do:

(: "${1:?Where\'s my argument?}") || return && set -- "$1"

...which will handle that as well. But, in my opinion, a shell function should never affect an interactive shell unless absolutely necessary - like if it needs to alter a current shell variable or something. And so better than the above would be to declare the function as a subshell, like:

fn() (set -- "${1:?Where\'s my argument?}" && echo "$1")

...does.

Another way to do this is to accept all arguments and concatenate them - so all arguments are only treated as one in every case. You can do that like:

fn() (                              
    : "${1:?ERR: Where\'s my argument?}"
    set -- "$*" && echo "$1" 
)
fn '' ; fn here are a lot of arguments that will all be treated as one
###OUTPUT###
sh: line 2: 1: ERR: Where's my argument?
here are a lot of arguments that will all be treated as one

I personally don't much like the sh: line 2: 1: bit that usually happens - at least not in interactive shells. I usually stick a CTRL+V CTRL+M in before ERR: so it does a return to the head of the line in a terminal and writes over that bit but still outputs the more useful info into a log. Like:

fn() (                              
    : "${1:?^MERR: Where\'s my argument?}"
    set -- "$*" && echo "$1" 
)
fn ; fn 2>&1 | cat -A
###OUTPUT###
ERR: Where's my argument?
sh: line 2: 1: ^MERR: Where's my argument?$
mikeserv
  • 58,310
  • I liked the ${1:^MERR: text} solution but it does not clear to end of line so if the text is less than the script: line#: error_string it will leave garbage out there – johnnyB Apr 05 '15 at 09:41
  • @johhnyb Do something with ^[[2K then maybe. It should be noted though that the output device might not be a terminal after all. You can check it like [ t 2 ] though. – mikeserv Apr 06 '15 at 02:57
  • Beautify! that works in most terminals which clears to end-of-line. so now the msg looks nicer. went from Account="${1?-Please provide account name}" to Account="${1?^M$PROGRAM: ERROR: Please specify account^[[K}"... the users does not have to see the line number the error occurred on. – johnnyB Apr 07 '15 at 21:17