1

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.

noterudite
  • 37
  • 4
  • 1
    Btw.: this is no comment: `/* bunch of if/else statements and some loops*/` – Cyrus Oct 03 '20 at 12:39
  • I am willing to bet your $NICE (notice caps) is not initialized / incorrectly initialized or something happens in the "bunch of if else statements". If it doesn't work, please post minimum, reproducible code and I can check. – Anon Oct 03 '20 at 12:43
  • 2
    That's the error I would expect if you had *quoted* `$nice_val`. The entire value is being treated as a single word. – chepner Oct 03 '20 at 12:46
  • 1
    The value of `PATH` is irrelevant if you are using an absolute path for the command name. A `PATH` issue would show up if you were simply running `nice -n -19` as a 'command not found', not 'no such file or directory'. – chepner Oct 03 '20 at 12:49
  • 2
    As is, nobody can reproduce your problem using only the code you have shown. – chepner Oct 03 '20 at 12:50
  • Hey guys. Thank you for responding. Apologies for the incomplete code. Its a pretty big script. (I'm new to stack overflow.) I missed resetting the IFS and that was causing the issue. I found Gordon Davisson's answer helpful. Thank you for your time. – noterudite Oct 03 '20 at 18:30
  • @dg_ If the answer is helpful then please accept it. – kaylum Oct 03 '20 at 21:50

1 Answers1

4

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 "$@"
     }
    
Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • Yes, fantastic observation. It turns out that IFS was indeed modified to ","(comma) and was never set back to its default space. This was causing the incorrect expansion of $nice_val like you mentioned. I reset the IFS to space and it works fine now. Thanks so much. Really appreciate it. – noterudite Oct 03 '20 at 18:07
  • @dg_ I'm glad that solved it. BTW, I added a note about isolating `IFS` changes to a single command; if that'd work in your case, I consider it a cleaner solution. Also, since this did solve the problem, please consider [accepting this answer](https://stackoverflow.com/help/someone-answers). – Gordon Davisson Oct 03 '20 at 19:58