1

I have a shell script in /usr/local/bin

#!/bin/sh

extract() {
 if [ -z "$1" ]; then
    # display usage if no parameters given
    echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
 elif [ -f "$1" ]; then
        case $1 in
          *.tar.bz2)   tar xvjf "$1"    ;;
          *.tar.gz)    tar xvzf "$1"    ;;
          *.tar.xz)    tar xvJf "$1"    ;;
          *.lzma)      unlzma "$1"      ;;
          *.bz2)       bunzip2 "$1"     ;;
          *.rar)       unrar x "$1"     ;;
          *.gz)        gunzip "$1"      ;;
          *.tar)       tar xvf "$1"     ;;
          *.tbz2)      tar xvjf "$1"    ;;
          *.tgz)       tar xvzf "$1"    ;;
          *.zip)       unzip "$1"       ;;
          *.Z)         uncompress "$1"  ;;
          *.7z)        7z x "$1"        ;;
          *.xz)        unxz "$1"        ;;
          *.exe)       cabextract "$1"  ;;
          *)           echo "extract: '$1' - unknown archive method" ;;
        esac
        echo "extraction successful: $1"
    else
        echo "$1 - file does not exist"
fi
}

However, it doesn't run properly unless I do source /usr/local/bin/extract in the current terminal. I know /usr/local/bin is in my path.

I have tried to change owner of the script to root:root to see if that makes a difference. File permissions are 755. I have tried to source /usr/local/bin again in my .zshenv but it was already in $PATH anyway. The only way I can make it work is if I use source /usr/local/bin/extract directly. I have tried rebooting after various changes like this. I checked the file for bashisms in shellcheck. I tried with different shebangs, #!/usr/bin/env bash and #!/bin/sh. my /bin/sh is linked to dash.

I expected any script or binary in /usr/local/bin to work without sourcing it for each terminal sessions.

Brandon Johnson
  • 172
  • 1
  • 6
  • 1
    Not sure why the question was downvoted. I provided a legitimate question after searching for answers. I also provided the solution to the problem after experimenting for a while. Furthermore, I provided the problem, what I tried, and what I expected. It was a simple mistake, but I feel like it could happen to anyone who hasn't already experienced such an issue or wasn't previously educated on how to handle this properly. – Brandon Johnson Jul 29 '23 at 07:06
  • 2
    If this is really is the **whole** script, it does not do anything, and I wonder why it ended up in `/usr/local/bin`. It definitely shouldn't be there. I suggest that you search the culprit who placed it in this directory and kindly ask him to leave the company. Basically, the script just defines a function, but that's all. So when you call it as a child process, there is no observable behaviour (except eating some CPU cyles). If you **source** the script, it still doesn't do anything, but at least it places the function `extract` into your shell process, so you can call `extract` afterwards. – user1934428 Jul 31 '23 at 07:48
  • 1
    _Not sure why the question was downvoted._ I don't know for sure, but you tagged the question as _bash_ and _zsh_, but don't explain how it is related to those two languages. More importantly, you just said "it does not run properly", but you don't explain what you would expect from a "proper" execution. – user1934428 Jul 31 '23 at 07:50

1 Answers1

3

I realized the problem shortly after posting this. All the code was inside of a function and it was never called. It worked when I sourced the file because the function came into the local scope of my current shell. I deleted the function wrapper and put it as raw code in the shell script e.g.

if [ -z "$1" ]; then
    # display usage if no parameters given
    echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
 elif [ -f "$1" ]; then
        case $1 in
          *.tar.bz2)   tar xvjf "$1"    ;;
          *.tar.gz)    tar xvzf "$1"    ;;
          *.tar.xz)    tar xvJf "$1"    ;;
          *.lzma)      unlzma "$1"      ;;
          *.bz2)       bunzip2 "$1"     ;;
          *.rar)       unrar x "$1"     ;;
          *.gz)        gunzip "$1"      ;;
          *.tar)       tar xvf "$1"     ;;
          *.tbz2)      tar xvjf "$1"    ;;
          *.tgz)       tar xvzf "$1"    ;;
          *.zip)       unzip "$1"       ;;
          *.Z)         uncompress "$1"  ;;
          *.7z)        7z x "$1"        ;;
          *.xz)        unxz "$1"        ;;
          *.exe)       cabextract "$1"  ;;
          *)           echo "extract: '$1' - unknown archive method" ;;
        esac
        echo "extraction successful: $1"
    else
        echo "$1 - file does not exist"
fi

and now it works fine. I would like to know if there is a way to keep this as a function? I think I'll test to see if I can just call extract() at the end of the script.

EDIT:

Thanks to Gordon's comment, I figured out how to keep it a function and call it properly. I should not have named the function the same name as the script, and I learned how to call the function properly and pass the arguments from the script call to the function.

#!/bin/sh
extractfunc() {
if [ -z "$1" ]; then
   # display usage if no parameters given
   echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
elif [ -f "$1" ]; then
       case $1 in
         *.tar.bz2)   tar xvjf "$1"    ;;
         *.tar.gz)    tar xvzf "$1"    ;;
         *.tar.xz)    tar xvJf "$1"    ;;
         *.lzma)      unlzma "$1"      ;;
         *.bz2)       bunzip2 "$1"     ;;
         *.rar)       unrar x "$1"     ;;
         *.gz)        gunzip "$1"      ;;
         *.tar)       tar xvf "$1"     ;;
         *.tbz2)      tar xvjf "$1"    ;;
         *.tgz)       tar xvzf "$1"    ;;
         *.zip)       unzip "$1"       ;;
         *.Z)         uncompress "$1"  ;;
         *.7z)        7z x "$1"        ;;
         *.xz)        unxz "$1"        ;;
         *.exe)       cabextract "$1"  ;;
         *)           echo "extract: '$1' - unknown archive method" ;;
       esac
       echo "extraction successful: $1"
   else
       echo "$1 - file does not exist"
fi
}
extractfunc "$@"

The differences are that I renamed the function to extractfunc() and called it with extractfunc "$@" so all arguments when calling the script will be passed to the function call.

Brandon Johnson
  • 172
  • 1
  • 6
  • 2
    Don't give the function the same name as the script itself, that's just asking for more confusion. If you do want to function-ize it within the script, you'd put something like `extractfunc "$@"` to pass on the script's arguments (see [this](https://stackoverflow.com/questions/4824590/propagate-all-arguments-in-a-bash-shell-script) or [this](https://stackoverflow.com/questions/3811345/how-to-pass-all-arguments-passed-to-my-bash-script-to-a-function-of-mine)). – Gordon Davisson Jul 29 '23 at 05:23
  • @GordonDavisson okay thank you, I will read those! I was playing with this for a while and couldn't figure it out, and couldn't find anything about it. – Brandon Johnson Jul 29 '23 at 05:25
  • It worked! I renamed the function to extractfunc() and added `extractfunc "$@"` to the end. Thank you so much! – Brandon Johnson Jul 29 '23 at 05:34
  • In fact, don't go sticking random scripts into `/usr/local/bin`. You want this to be a function, which is in your `~/.bashrc` or whatever profile file. – Kaz Jul 29 '23 at 05:52
  • @Kaz I put it in /usr/local/bin so it would be available to anyone on the PC, not just myself – Brandon Johnson Jul 29 '23 at 05:54