/usr/bin/nice -n -19: No such file or directory but nice exists

2.3k views Asked by At

I have a small function in a bash script like this,

func()
{
    
    local nice_val="$NICE -n -19"
    
    /* bunch of if/else statements and some loops*/
    
    $nice_val $NOHUP a.out >> $log_file 2>&1 &
}

when i try to execute this file, i see this error. /root/bringup.sh: line 323: /usr/bin/nice -n -19: No such file or directory

Here are a few things i verified,

  1. Yes, nice executable exits in /usr/bin/nice
  2. my $PATH also contains /usr/bin/
  3. checked if i'm missing any libs, i dont think i am.
root@dg:~# ldd /usr/bin/nice
        linux-vdso.so.1 (0x00007ffdc4dab000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f749b9bf000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f749bf67000)
root@dg:~# find / -name libc.so.6
/lib/x86_64-linux-gnu/libc.so.6

Note: 1.I'm running everything as root and $NICE, $NOHUP are some of the shell variables that gets sourced (using "source") in the beginning of the script. 2.This script gets called by another script 3.I read online clearing the hash (hash -r) might help if my "nice" was moved around but did not help. 4. All of this scripts are run inside of a container. But i guess that should not impact anything.

In the above snippet, if i replace $nice_val with the complete path to "nice", it works. i.e /usr/bin/nice -n -19 $NOHUP a.out >> $log_file 2>&1 & ---> Surprisingly this works. Oh, and i checked if i have any extra spaces, /r's. I don't see any of that.

I really cant wrap my head around what's going wrong. Any insight into this problem is very much appreciated. Thanks so much.

UPDATE: This was the problem with my code: (This is a condensed dummy code that reproduces the issue) File 1: bringup.sh

#! /bin/bash

declare -r NOHUP="/usr/bin/nohup"
declare -r NICE="/usr/bin/nice"
declare -r CAT="/bin/cat"

log_file="/root/affinity/dumplog"
values_file="/root/affinity/values"
niceval="$NICE -n -10"

get_indexed_val()
{
    local val=$1
    if [ -e $values_file ]; then
        local val_str=$($CAT $values_file)
        IFS=','
        read -ra cpu_array <<< "$val_str"
        #
        # If you uncomment the below line(setting back the IFS to 'space', code works fine.
        #
        #IFS=' '
        return ${cpu_array[$val]}
    fi
    return 0
}

index=0
for (( index=0; index < 4; index++ )); do
    get_indexed_val $index
    myval=$?
    $niceval $NOHUP /root/affinity/loop.sh $myval >> $log_file 2>&1 &
done

File 2: loop.sh

#! /bin/bash

i=0
number=$1
while [ $i -lt $number  ]; do
        sleep 0.1
done

File 3: values

140,150,160,170

The error message you'll see if you dont reset the IFS.

./bringup.sh: line 28: /usr/bin/nice -n -10: No such file or directory
./bringup.sh: line 28: /usr/bin/nice -n -10: No such file or directory
./bringup.sh: line 28: /usr/bin/nice -n -10: No such file or directory
./bringup.sh: line 28: /usr/bin/nice -n -10: No such file or directory

Thank you all. Much appreciated.

1

There are 1 answers

2
Gordon Davisson On BEST ANSWER

The error message you're getting, /root/bringup.sh: line 323: /usr/bin/nice -n -19: No such file or directory, indicates that " -n -19" is being treated as part of the filename, rather than as parameters. Since there's no file named "nice -n -19" in the /usr/bin directory, you get a file not found error.

Normally, when you expand a variable that contains spaces (or other whitespace) and don't have double-quotes around it, the variable's value will be split into "words" based on whitespace. In this case, I'd expect it to be split into "/usr/bin/nice", "-n", and "-19", and so the first would be treated as the command/filename to run and the others as parameters. But in this case the splitting is apparently not happening. I see several possible explanations:

  • IFS has been changed. The IFS variable defines which characters are considered whitespace for word-splitting purposes; if it doesn't contain the space character, that would explain why the value isn't getting split normally.

    Changing IFS can have a lot of weird effects like this, so if you need to change it for some reason, it's best to set it back to normal as soon as possible afterward.

    It's also sometimes sufficient to apply an IFS change only to a single command, by making the setting a prefix to that command. For example:

     IFS=, read -r field1 field2 field3
    

    will split fields on commas, but since the IFS setting applies specifically to that command it's not necessary to set it back afterward. But this doesn't always do what you expect, because the setting applies to the execution of that command, not to how its arguments are parsed. For example, IFS=, echo $var will split the value of $var on normal whitespace, and then set IFS while echo prints the results (so the setting has no actual effect).

  • Those characters in the string might not be normal spaces, but something similar-looking-but-different, like non-breaking spaces. You can check by doing a hex dump of the text. Here's an example (with ^^s added by me):

     $ echo "/usr/bin/nice -n -19" | xxd    # These are normal spaces
     00000000: 2f75 7372 2f62 696e 2f6e 6963 6520 2d6e  /usr/bin/nice -n
                                               ^^                    ^
     00000010: 202d 3139 0a                              -19.
               ^^                                       ^
     $ echo "/usr/bin/nice -n -19" | xxd    # These are non-breaking spaces
     00000000: 2f75 7372 2f62 696e 2f6e 6963 65c2 a02d  /usr/bin/nice..-
                                               ^^ ^^                 ^^
     00000010: 6ec2 a02d 3139 0a                        n..-19.
                 ^^ ^^                                   ^^
    

    See how the second dump has "c2 a0" instead of "20" in the hex, and ".." instead of just " " in the text on the right? Those are non-breaking spaces in UTF-8 encoding. Try putting echo "$nice_val" | xxd in your script, and see what it prints.

  • The script is running under zsh (rather than bash). By default, zsh doesn't do word-splitting (because it actually causes a lot of bugs and there are usually better ways to do what's needed).

  • You actually have double-quotes around the variable reference, and just didn't include them in the question. If this were the actual command:

     "$nice_val" $NOHUP a.out >> $log_file 2>&1 &
    

    ...then the error message would make perfect sense. Note that normally, putting double-quotes around variable references is a good idea (to avoid the problems word-splitting can filename globbing can cause), but in this case you're counting on it.

    But storing commands in variables like this is somewhat unreliable anyway (see BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!). It might be better to define functions instead of variables for your NICE and NOHUP "commands". Something like this:

     nice_val() {
         /usr/bin/nice -n -19 "$@"
     }