DAY 033: COMMAND$
#1
Ever wish we could travel the world on a transporter beam? Well, we can't, so for now, let's stay home and play with COMMAND$...

SYNTAX: cmd$ = COMMAND$[(count%)]

USE: Passing arguments to other apps and drag and drop applications.

And as a bonus, if you act now, we'll throw in _COMMANDCOUNT to evaluate that count% variable.

Okay, let's get our demo of the day going...

Works for Windows, untested for Linux or Mac...
Code: (Select All)
WIDTH 120, 25
_SCREENMOVE 0, 0
PRINT
cmd$ = COMMAND$(0) ' Gets the complete path and name of your current running program.
j = _INSTRREV(cmd$, "\")
program$ = MID$(cmd$, j + 1) ' Parse to just the program name.

IF j THEN
    DrivePath$ = MID$(cmd$, 1, LEN(cmd$) - LEN(program$))
    j = 1
ELSE
    DrivePath$ = COMMAND$(1)
    j = 2
END IF

PRINT "Drive and path of program passing arguments... "; DrivePath$: PRINT
PRINT "Program name passing the arguments............ "; program$: PRINT

IF LEN(COMMAND$(1)) THEN
    ' Parse and add back spaces because command$ needs passing spaces enclosed in quotes to preserve them.
    k = _COMMANDCOUNT ' The number of arguments passed.
    FOR i = j TO k
        cmd$ = COMMAND$(i) ' Parse out an argument.
        msg$ = msg$ + cmd$ + " " ' Concatenate string and add space back into string.
    NEXT
    msg$ = MID$(msg$, 1, LEN(msg$) - 1) ' Cut off the trailing space we added.
    COLOR 14: PRINT msg$: PRINT: COLOR 7
END IF

PRINT "[1] Pete is a genius!": PRINT
PRINT "[2] Steve is a genius!": PRINT
PRINT "[3] Pete and Steve are both idiots, and I wore out my [3] key!": PRINT
PRINT
DO
    INPUT "1-3: ", ans%: PRINT
    IF ans% = 0 THEN SYSTEM
    IF ans% > 0 AND ans% < 4 THEN EXIT DO
LOOP

' RUN and pass our sentences as a command$() parameter.
' Note we need a space between each argument we are going to be passing.
format$ = " " + CHR$(34) + _CWD$ + CHR$(34) + "\ "
SELECT CASE ans%
    CASE 1: RUN program$ + format$ + "Pete is a genius!"
    CASE 2: RUN program$ + format$ + "Steve is a genius!"
    CASE 3: RUN program$ + format$ + "Pete and Steve are both idiots, and I wore out my [3] key!"
END SELECT

So the example above is a self-contained program. You don't have to name it or save it, just run it. Well how in the hell is that possible, you ask? Well, let's get into that, pronto...

COMMAND$(0): This is a special parameter of COMMAND$() that retrieves the current drive, path, and name of the running app. Pretty cool, right?

Edit: Steve caught the fact COMMAND$(0) doesn't grab the drive and path of the passing app, it grabs where it is being started from along with the name, of course. (See posts below for more info.) So I adjusted the code above to also include a way to pass the drive and path with _CWD$. Our example only needs the name, but it's nice to know the drive and path, if needed, could be passed as well.

Now that our example program knows its name, it let's you pick a selection then runs itself. It passes the choice you made through COMMAND$() and parses it out in the first conditional block statement... IF LEN(COMMAND$(1)) THEN

The (1) of COMMAND$(1) is string we passed in the RUN statement. So COMMAND$(0) got us or drive, path, and program name, and COMMAND$(1) got us the string argument we passed in the RUN statement.

Note: A difference between QB64 and QuickBASIC is QB made the arguments uppercase. QB64 maintains the case of the passed string.

_COMMANDCOUNT then kicks in and tells us how many parameters. COMMAND$() separates our text message by spaces. In other words a space acts as a string delimiter. So if the message was: "Call me Betty" we would have three arguments: Call, me, Betty. By knowing we have 3, we can save a little coding parsing those arguments out as COMMAND$(1), COMMAND$(2), and COMMAND$(3) by using our FOR/NEXT loop routine.

So even though we posted just one elf-contained program, for ease of use, keep in mind we will primarily use COMMAND$() when RUNning or SHELLing to another application, which needs info passed to it from the previous or calling program.

Note: Other methods of accomplishing for RUN and/or SHELL would be CHAIN, but not used in SHELL statements, Piping, used in SHELL statements, _CLIPBOARD$, usable in both, database sharing, used in both, TCP/IP, usable in both, and SCREEN writing and reading, used in both.

Oh, and here is a fun little shortcut example you can make to view files from Explorer in Notepad...

Instructions: name it anything you want. Make exe only. Find exe and make Desktop shortcut.
Code: (Select All)
cmd$ = COMMAND$(0) ' Gets the complete path and name of your current running program.

IF LEN(COMMAND$(1)) THEN
    ' Parse and add back spaces because command$ needs passing spaces enclosed in quotes to preserve them.
    j = _COMMANDCOUNT ' The number of arguments passed.
    FOR i = 1 TO j
        cmd$ = COMMAND$(i) ' Parse out an argument.
        msg$ = msg$ + cmd$ + " " ' Concatenate string and add space back into string.
    NEXT
    msg$ = MID$(msg$, 1, LEN(msg$) - 1) ' Cut off the trailing space we added.
    COLOR 14: PRINT msg$: PRINT: COLOR 7
    PRINT: PRINT "SHELL notepad " + cmd$
    SHELL _HIDE _DONTWAIT "notepad " + cmd$
END IF

So what now? Open Explorer and find a text file. Drag it into the desktop icon you just created. It will open that dragged file in Notepad.

...And if you want to make it stealth, code it as...
Code: (Select All)
' Make a desktop shortcut to this and drag files into it.
_SCREENHIDE
cmd$ = COMMAND$(0) ' Gets the complete path and name of your current running program.
REM PRINT cmd$: PRINT
IF LEN(COMMAND$(1)) THEN
    ' Parse and add back spaces because command$ needs passing spaces enclosed in quotes to preserve them.
    j = _COMMANDCOUNT ' The number of arguments passed.
    FOR i = 1 TO j
        cmd$ = COMMAND$(i) ' Parse out an argument.
        msg$ = msg$ + cmd$ + " " ' Concatenate string and add space back into string.
    NEXT
    msg$ = MID$(msg$, 1, LEN(msg$) - 1) ' Cut off the trailing space we added.
    REM COLOR 14: PRINT msg$: PRINT: COLOR 7
    REM PRINT: PRINT "SHELL notepad " + cmd$
    SHELL _HIDE _DONTWAIT "start notepad " + cmd$
END IF
SYSTEM

Now imagine what other drag and drop apps you could use this for. Paint.net is one I use this on. I use SENDKEYS WIn32 API routines in my COMMAND$() desktop app to control PAINT.net after it opens and loads the image I dragged into the file. That saves me several steps. I can even resize photos without touching the keyboard once!

If you guys have other uses or programs of a similar nature to share, please feel free to either add them here as examples, or add the link to the forum page.

Thanks, an now back to work on my transporter beam. I think I just sent my CapsLock toe to the South of France.

Pete
Reply
#2
Quote:COMMAND$(0): This is a special parameter of COMMAND$() that retrieves the current drive, path, and name of the running app. Pretty cool, right?

The correct answer here is: [4] Pete is wrong!  Tongue

Command$(0) does NOT retrieve the current drive, path, and name of the running app.  He's right though, it'd be pretty cool if that's what it did for you -- but it's not.

What Command$(0) does do is return the command which the user used to start your program.  Let me explain, so it's not confusing to anyone.

Let's say you open up File Explorer and navigate to "C:\QB64PE\" and from there you click on "qb64pe.exe" to start up our IDE like usual.  At that point, file explorer would use "C:\QB64PE\qb64pe.exe" to start the program which you clicked on.  COMMAND$(0) would have in it, "C:\QB64PE\qb64pe.exe" -- which is where Pete is basing his misinformation off from.

Now, here's a different scenario:   

Let's say you open a command prompt.  You type in "CD C:\QB64PE".  You then type in "qb64pe" to run the executable inside the folder that you just navigated to.  COMMAND$(0), in this case, is simply going to be "qb64pe".

COMMAND$(0) simply returns to you the base command of how the user started your program.  In most cases, it'll be as Pete suggests, as most people tend to start programs via the File Explorer -- BUT THAT'S NOT GUARANTEED!!

All you can depend on with COMMAND$(0) is getting back whatever command the user entered to start your program.  Drive, path, and other such information is not guaranteed.

If you want the drive and path, use _CWD$ at the start of your program and store it in a variable if necessary, before something changes its value.  _CWD$ always *starts* in the directory where your EXE is located, but there's no guarantee that it'll remain there.

Code: (Select All)
MyAppDir$ = _CWD$
CHDIR "C:\"
PRINT MyAppDir$, _CWD$

With the above, you can easily see what I'm talking about with the _CWD$.

With COMMAND$(0), you should always be able to get back the name of your EXE (in case the user renamed your program), but if you want the drive and path, use _CWD$ at the start of the program to get it.  You can't be for certain that COMMAND$(0) will have that information for you at all.
Reply
#3
Nice catch!

I just tried this...

Made a routine with COMMAND$() and saved it as untitled.exe

Opened another instance of the IDE: RUN "untitled.exe"

It only showed the "untitled.exe" program name returned for COMMAND$(0).

So I then typed the full drive and path... RUN "C:\QB64PE\untitled.exe"

And it returned "C:\QB64PE\untitled.exe"

So it's the calling info it's collecting, rather than the actual drive and path of the app passing the arguments. That's needed for building any project that would not provide the full drive and path info.

So a more bullet proof method would be to pass the drive and path with _CWD$, something like..

Code: (Select All)
RUN "untitled.exe " + _CWD$

It needs to be a bit more involved. Add a space to pass it with other info, etc., but working with it should insure the correct drive and path from the calling program.

So I checked the first snippet and also found COMMAND$(0) include its own drive and path when it is run from the QB64 IDE, but when it RUNs itself, it only includes the program name.

I will make some edits accordingly in my original post for this, thanks!

Looks like #2 was the right choice in that app, today.

Pete
Reply
#4
Thumbs Up 
I like the way this "COMMAND$" was changed for QB64.

With QuickBASIC and MS-DOS it was impossible to get lowercase letters from the terminal prompt. Although that's not the reason why the GNU people responsible for "getopt" library decided to create the "long" options besides cryptic switches like "-rf" and "-Fs" ...

Before making "COMMAND$" like an array there was the task of creating a dynamic string array to process the whole "COMMAND$" value according to spaces. It worked well, but I easily abandoned it in favor of "COMMAND$(1)" and so on.

The "flaw" with "COMMAND$(0)" was inherited from the C runtime library and some ancient libraries and scripts for Unix and descendants actually depend on it.
Reply
#5
(12-13-2022, 07:53 PM)Pete Wrote: Nice catch!

I just tried this...

Made a routine with COMMAND$() and saved it as untitled.exe

Opened another instance of the IDE: RUN "untitled.exe"

It only showed the "untitled.exe" program name returned for COMMAND$(0).

So I then typed the full drive and path... RUN "C:\QB64PE\untitled.exe"

And it returned "C:\QB64PE\untitled.exe"

So it's the calling info it's collecting, rather than the actual drive and path of the app passing the arguments. That's needed for building any project that would not provide the full drive and path info.

So a more bullet proof method would be to pass the drive and path with _CWD$, something like..

Code: (Select All)
RUN "untitled.exe " + _CWD$

It needs to be a bit more involved. Add a space to pass it with other info, etc., but working with it should insure the correct drive and path from the calling program.

So I checked the first snippet and also found COMMAND$(0) include its own drive and path when it is run from the QB64 IDE, but when it RUNs itself, it only includes the program name.

I will make some edits accordingly in my original post for this, thanks!

Looks like #2 was the right choice in that app, today.

Pete

Your "bulletproof" method is a wee too complicated for my needs. All the parts are there for you; just just need to collect and assemble them for yourself:

Code: (Select All)
If InStr(_OS$, "WIN") Then slash$ = "\" Else slash$ = "/"
MyAppDir$ = _CWD$
MyAppName$ = Mid$(Command$(0), _InStrRev(Command$(0), slash$) + 1)
Print "My program directory is: "; MyAppDir$
Print "My program name is: "; MyAppName$
Print "My slash is: "; slash$
Print
Print "Put them all together for: "; MyAppDir$ + slash$ + MyAppName$

You might want to error check to make certain that the MyAppName$ has a ".exe" on the end of it for Windows (Linux and Mac don't use .exe), but otherwise that's the simple process to get your starting path, slash, and app name.
Reply
#6
@Pete:  One thing to remember:  _CWD$ only holds your path at start up.  As I mentioned earlier, it can change later.   If you need to preserve that information, be certain to save it in a variable as I did above with MyAppDir$.
Reply
#7
I learned something here.  Like Pete, I also thought COMMAND$(0) showed the drive+path too.  It sure pays to read all of these keyword day posts.

- Dav

Find my programs here in Dav's QB64 Corner
Reply
#8
yeah, the elucidations by Steve are of really high quality.  I wonder if this will find its way into The QB64 Bible or perhaps PE IS the QB64 bible?
Reply
#9
(12-14-2022, 01:31 AM)vince Wrote: yeah, the elucidations by Steve are of really high quality.  I wonder if this will find its way into The QB64 Bible or perhaps PE IS the QB64 bible?

I tend to think of the PE Forums and Wiki as being the definitive QB64 Bible.  Things written in a pdf, or Word document, are static and never changing.  Once you print a page out, you're stuck with what the ink on the page says.  The forums and wiki are dynamic.  If something is wrong, it can be fixed.  If the language changes, both the forums and the wiki can be updated to reflect those changes.  THEY are the true places to find the gospel for all things QB64-PE.

Everything else -- including my "QB64 Bible" -- is a pale shadow of the truth you'll find between our forums and our wiki.  Wink
Reply
#10
(12-14-2022, 01:43 AM)SMcNeill Wrote: I tend to think of the PE Forums and Wiki as being the definitive QB64 Bible.  Things written in a pdf, or Word document, are static and never changing.  Once you print a page out, you're stuck with what the ink on the page says.  The forums and wiki are dynamic.  If something is wrong, it can be fixed.  If the language changes, both the forums and the wiki can be updated to reflect those changes.  THEY are the true places to find the gospel for all things QB64-PE.

Everything else -- including my "QB64 Bible" -- is a pale shadow of the truth you'll find between our forums and our wiki.  Wink

Only if Internet connections were as durable as life and the ability to have one. Otherwise I have to insist on static pages. I have the privilege now but might not in the near future, so I have to enjoy these means while I still could.
Reply




Users browsing this thread: 1 Guest(s)