Search code examples
pythonbashzsh

Custom bash function not feeding python multiple args


The problem:

I'm writing a program that performs actions in python based on links, and possibly expanding it to do things beyond that. This program is meant to be quickly used through bash. So, I'm using a custom function to do that.

function youtube() {python3 <youtube program path here> $1}

As for the python file; I'm using sys, os, and re in order to make it function. sys, in order to use both sys.exit() and var = sys.argv[<argNum>], the former in order to exit the program using custom exceptions, like error.searchError() or error.usageError(), and the later for actualling using the arguments from the command itself. os is just for os.system('{}'.format(<your command here>)). And re is for removing the spaces from the second argument, where my problem lies, and replacing them with '+', as per query = re.sub(' ', '+', query).

Now, as for the problem itself. As I mentioned before, the problem lies with the second bash argument, or sys.argv[2]. With sys.argv[0] being the python file, and sys.argv[1] being the option, in this case being -s.

sys.argv[2] is meant to be the actual youtube search query. But, according to whenever I use the command with all three arguments, youtube -s Hi this is a test., I get the following output, as per the custom error I made: No search query provided!. This only happens when python excepts an IndexError, which means that the program is not receiving the second argument from bash or zsh. What is actually supposed to happen, when the second arguments does exist, is:

os.system('open https://www.youtube.com/results?search_query=Hi+this+is+a+test.')

Which opens that link in my default browser. I have tried to add $2 to the custom function, and various ways of entering the second argument through the python source itself, including using a x = input('Search Query: ') statement. But that isn't optimal for what I'm doing.

The code:

The following is the source code for all the nonsense I just typed out.

The custom function:

function youtube() {python3 <python program path here> $1}

For those that have no idea what this means (i.e.; people that don't know much (or anything) about bash coding); The function method creates a bash object, in this case, youtube(). As for the code in the brackets ({}), this uses the function python3, which just pushes the program in argument 0 to the python 3.x interpreter, to open <python program path here>, which is a placeholder for the actual path of the program. As for $1, this is a variable that always equals the text inputted after the function name.

The custom errors:

class error:

    def usageError():
        usageError = '''Usage: youtube [-s] [<search_query>]
Help: Performs actions related to https://www.youtube.com
Options:
        -s              Opens https://www.youtube.com/results?search_query=<your query here>'''

        print(usageError)
        sys.exit()

    def searchError():
        searchError = 'No search query provided!'

        print(searchError)
        sys.exit()

Is this irrelevant? I'm not sure, but I'm putting it in anyway! Now, if you don't understand it, the following should explain it.

The error class contains all of the customs errors for this program, ok? Ok, so you get that, but what do these functions do? usageError is raised when argument 1 simply doesn't exist, and prints the usage information to the terminal. Then sys.exit()s the program, basically the equivalent of hitting Alt+f4 in video game. searchError, on the other hand, only happens if argument 2 doesn't exist, meaning there is no search query. It then tells you that you're stupid, and will need to actually enter your query for it to work.

Well, maybe not that exactly, but you get the point.

The Juicy Bits:

option = ''

try: option = sys.argv[1];
except IndexError: raise error.usageError()

if option == '-s':

    try:
        query = sys.argv[2]
        query = re.sub(' ', '+', query)
        os.system('open https://www.youtube.com/results?search_query={}'.format(query))
    except IndexError: raise error.searchError();

Just to explain; First, the program creates the variable option and then sets it to an empty string. Then, it tries to set option to argument 1, or the option. If argument 1 doesn't exist, it raises the error error.usageError, as explained in The Custom Errors. After that, the program tries to create the variable query, and set it to argument 2, then replace all of the spaces in query with '+' signs. If all of that succeeds to happen, it then loads up the youtube search in your default browser. If not, it raises the error error.searchError().

The Edits

Edit 1. The error was in The Custom Function. Where I should have had an $@, I had an $1. As Jeshurun Roach explains in his answer, $1 only holds the argument 1, and no other arguments. While $@ contains all variables.

function youtube() {python3 <python program path here> $@}

Solution

  • $1 refers to the first argument passed into the function. in bash, spaces delimit arguments. so in your example youtube -s Hi this is a test., $1 is -s, $2 is Hi, $3 is this etc...

    What you're looking for is the $@ symbol. This value stands for all the arguments.

    But just plugging in $@ instead of $1 won't fix all your problems. in your python script, each argument will be broken up again by spaces, just like the bash function.

    To fix this, you can put quotes around the text after the flag like so: youtube -s 'Hi this is a test.'.