QB64 Phoenix Edition
DAY 035: SHELL - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: Official Links (https://qb64phoenix.com/forum/forumdisplay.php?fid=16)
+--- Forum: Learning Resources and Archives (https://qb64phoenix.com/forum/forumdisplay.php?fid=13)
+---- Forum: Keyword of the Day! (https://qb64phoenix.com/forum/forumdisplay.php?fid=49)
+---- Thread: DAY 035: SHELL (/showthread.php?tid=1282)



DAY 035: SHELL - Pete - 12-16-2022

You might think this keyword was leftover from TurtleBASIC, but you'd be wrong. It's actually from DOS.

SHELL is a very versatile way to either call a DOS command or run another app while staying in your current program.

SYNTAX SHELL [_DONTWAIT] [_HIDE] [DOSCommand$]

_DONTWAIT means continue on in our program flow instead of hanging until the call or program started is finished or terminated.

_HIDE means don't show the cmd.exe console window.

Hiding the console is usually preferred. If _HIDE prevents the program from launching, try adding cmd /c to the SHELL.

Waiting is useful if the app or DOS function you are accessing needs to pass information back to your program. SHELL "Dir" (and piped to a file - we'll discus piping later...) to get the directory info into the next part of your program, for instance.

So let's continue by discussing some other improvements from when this keyword was used in QBasic.

In Windows 98 and prior, we had to use COMMAND\c with our SHELLcall. command.exe was the command prompt then, but sometime after Windows 98, M$ remade the command prompt, and called it cmd.exe. We then used CMD /c in our SHELL calls. Well, along comes QB64 and guess what, cmd /c, forgetaboutit! We don't need it anymore, well, that isn't quite true. QB64 takes care of the command prompt call for us, but it fails once in awhile. (See the example marked INCLUDE PATH, a few lines below).

QB64 can use long path folder names and file names and SHELL command lines can be longer than 124 characters. (Wiki).

So now let's discuss some SHELL components and common ways to use SHELL:

A QBasic SHELL component we can still use in QB64 is START. START is an alternative to _DONTWAIT, and has one added advantages, switches...

Switches in a SHELL START to another app tell the operating how to launch it.

Commonly used switches:

/min Launch minimized to the taskbar.

/max Launch it full screen.

/p Send any text file called with the app to the printer.

Example: Launch Wordpad minimized to print a text file...

Remember, since we are using START we do not have to use _DONTWAIT in this example.

Code: (Select All)
SHELL _HIDE "START /min /p wordpad myfile.txt"

Now you might ask, "Hey, where's the path?" Well, in some, but certainly not all instances, registered programs can be automatically found by a SHELL call. For instance, my Firefox browser will launch without a path included. My advice, give it a try without a path, but if it cannot be found, include the path.

How to include a path in a SHELL call.

The easiest way is to include the drive an path.

Example: INCLUDE PATH

Code: (Select All)
SHELL _HIDE _DONTWAIT "cmd /c C:\Windows\Notepad.exe"

So hold on there Petey! What's up with the cmd /c in there? Well, in my system, I can't get: HELL _HIDE _DONTWAIT "C:\Windows\Notepad.exe" unless I add it. Go figure. My advice, SHELL is a bit fickle and you may have to play with some of these command line statements to get it to work.

Okay, now this is a PITA, but an important PITA. If you have a path with a space in it like PROGRAM FILES, you need to enclose that particular directory in quotes, using ASCII CHR$(34), the quote symbol, to avoid it being concatenated.

PRINT CHR$(34) + "Program Files" + CHR$(34)

So let's try to access Windows Photo View Device Imaging Devices...

Code: (Select All)
SHELL _HIDE _DONTWAIT "C:\" + CHR$(34) + "Program Files" + CHR$(34) + "\" + CHR$(34) + "Windows Photo Viewer" + CHR$(34) + "\ImagingDevices.exe"

Note we had to enclose two file paths with spaces in this example. Oh, and go figure, this one I could get to run with _HIDE and no cmd /c.

Anyway, we can save a little work by just enclosing both paths in quotes, one time...

Code: (Select All)
SHELL _HIDE _DONTWAIT "C:\" + CHR$(34) + "Program Files\Windows Photo Viewer" + CHR$(34) + "\ImagingDevices.exe"

So just remember, paths with spaces require quotes, paths without do not. I believe Linux and Mac have the option of using the apostrophe or CHR$(34).

So besides just running other apps while your program keeps running, SHELL can access command window functions, which you can PIPE into your program or into a file.

WARNING - Some of these examples make a file called mytemp.tmp. If you already have a file named mytemp.tmp, running any example with this file name will overwrite your file.

Code: (Select All)
$CONSOLE:ONLY
SHELL _HIDE "DIR /b *.bas>mytemp.tmp" ' The /b switch "bare" prints just the file names. > sign sends this info to the file.
OPEN "mytemp.tmp" FOR INPUT AS #1
DO
    LINE INPUT #1, a$
    PRINT a$
LOOP UNTIL EOF(1)
CLOSE #1

Okay, so how about instead of a file, we just pipe it to the clipboard?

Code: (Select All)
$CONSOLE:ONLY
SHELL _HIDE "DIR /b | clip"
PRINT _CLIPBOARD$

This example example will be to find that mytemp.tmp file in your qb64 folder. Be sure to change the example path to your path.

Code: (Select All)
SHELL _DONTWAIT "Explorer /select, C:\QB64PE\qb64pe3.3\qb64pe\mytemp.tmp"

Our last example is using SHELL to find our IP Address. This might not work on non-English platforms.

Code: (Select All)
' CAUTION - THE SHELL STATEMENT WILL OVERWRITE ANY PREEXISTING FILE YOU HAVE IN YOUR LOCAL DIRECTORY NAMED "mytemp.tmp".
' Change mytemp.tmp to something else if that's a problem.
SHELL _HIDE "ipconfig>mytemp.tmp"
OPEN "mytemp.tmp" FOR BINARY AS #1
a$ = SPACE$(LOF(1))
GET #1, , a$
CLOSE #1
DO
    j = INSTR(LCASE$(a$), search$)
    IPAddy$ = MID$(a$, j)
    IPAddy$ = MID$(IPAddy$, 1, INSTR(IPAddy$, CHR$(13)) - 1)
    IPAddy$ = _TRIM$(MID$(IPAddy$, INSTR(IPAddy$, ":") + 1))
    IF j AND LEN(search$) > 0 THEN
        PRINT "Your IP Address is: "; IPAddy$
        EXIT DO
    ELSE
        SELECT CASE cnt
            CASE 0
                search$ = "ipv4 address"
            CASE 1
                search$ = "ip address"
            CASE 2
                search$ = "ipv4 "
            CASE 3
                search$ = "ipv4-address"
            CASE 4
                search$ = "ip-address"
            CASE 5
                search$ = "ipv4-"
            CASE ELSE
                PRINT "Sorry, can't find IP addy. Opening Notepad so you can find it..."
                SHELL _HIDE "START Notepad mytemp.tmp"
                ' Other ways to write our SHELL...
                REM SHELL _HIDE _DONTWAIT "Notepad mytemp.tmp"
                REM SHELL _HIDE _DONTWAIT "cmd /c Notepad mytemp.tmp"
                REM SHELL _HIDE "cmd /c START Notepad mytemp.tmp"
                EXIT DO
        END SELECT
    END IF
    cnt = cnt + 1
LOOP

Pete


RE: DAY 035: SHELL - mnrvovrfc - 12-16-2022

(12-16-2022, 02:10 AM)Pete Wrote: In Windows 98 and prior, we had to use COMMAND/c with our SHELL call. command.exe was the command prompt then, but sometime after Windows 98, M$ remade the command prompt, and called it cmd.exe. We then used CMD /c in our SHELL calls. Well, along comes QB64 and guess what, cmd /c, forgetaboutit! We don't need it anymore, well, that isn't quite true. QB64 takes care of the command prompt call for us, but it fails once in awhile. (See the example marked INCLUDE PATH, a few lines below).

It was incredible that, as late as QB64 v0.98 the "SHELL" internals was checking for the existence of COMMAND.COM. I think it should have thrown an error in Windows8 or later, must have been silently ignored instead. I had no problems with it but it was one of those curiosities that persisted in Galleon's code.

Another thing that impressed me, in "libqb.cpp" was almost replicating the C++ code for "func_shell" another three times or so for the various modes, "_HIDE", "_DONTWAIT", and as a function. That was also done in various sections of the graphics routines, an awful lot of long hexadecimal numbers and bit-shift operators there.

Remember when the Wiki said that Galleon and others wanted QB64 to work on Windows98? Probably the desire to make it work on portable phones and screens destroyed that first wish.


RE: DAY 035: SHELL - mnrvovrfc - 12-16-2022

For the code examples obviously for Windows only, it might be better for the subject file to be placed in the system temporary directory. Because an user could decide to run a program like that in different directories each one having its "mytemp.tmp" or alike. Would have to deal with tedious concatenation making the code more difficult to read. Whoever is using an edition of QB64 that works on WindowsXP and earlier should look elsewhere. This code should work for Windows Vista and later and therefore no more worrying about a directory called "Documents and Settings" that long with spaces. The temporary path should begin with "C:\Users\<user's-name>\".

Edit: it could be spotty but this could work on Linux also except "wine" and space has to be added to the front of the whole entry, and all backslashes have to be doubled up. Maybe could use forward slash in place of backslash, but not for the ancient switches that should be delimited with hyphen instead. Also it's better to use "Z:" with Wine instead of "C:". The main problem remains being able to create a Windows EXE file in Linux to be able to test it more extensively.

Code: (Select All)
mypath$ = ENVIRON$("TEMP") + "\mytemp.tmp"
SHELL _HIDE "ipconfig>" + mypath$
OPEN mypath$ FOR BINARY AS #1
a$ = SPACE$(LOF(1))
GET #1, , a$
CLOSE #1
DO
    j = INSTR(LCASE$(a$), search$)
    IPAddy$ = MID$(a$, j)
    IPAddy$ = MID$(IPAddy$, 1, INSTR(IPAddy$, CHR$(13)) - 1)
    IPAddy$ = _TRIM$(MID$(IPAddy$, INSTR(IPAddy$, ":") + 1))
    IF j AND LEN(search$) > 0 THEN
        PRINT "Your IP Address is: "; IPAddy$
        EXIT DO
    ELSE
        SELECT CASE cnt
            CASE 0
                search$ = "ipv4 address"
            CASE 1
                search$ = "ip address"
            CASE 2
                search$ = "ipv4 "
            CASE 3
                search$ = "ipv4-address"
            CASE 4
                search$ = "ip-address"
            CASE 5
                search$ = "ipv4-"
            CASE ELSE
                PRINT "Sorry, can't find IP addy. Opening Notepad so you can find it..."
                SHELL _HIDE "START Notepad " + mypath$
                ' Other ways to write our SHELL...
                REM SHELL _HIDE _DONTWAIT "Notepad " + mypath$
                REM SHELL _HIDE _DONTWAIT "cmd /c Notepad " + mypath$
                REM SHELL _HIDE "cmd /c START Notepad " + mypath$
                EXIT DO
        END SELECT
    END IF
    cnt = cnt + 1
LOOP

I doubt that in this code anybody would need a "mytemp.tmp" dumped into the "Temp" directory deep in the user area. But we will never know.


RE: DAY 035: SHELL - Pete - 12-16-2022

Quote:Remember when the Wiki said that Galleon and others wanted QB64 to work on Windows98? Probably the desire to make it work on portable phones and screens destroyed that first wish.



Yes, the earliest versions of QB64 did work on Windows 98. I missed the ability to run QB64 on my Win98, but fortunately I also had an XP. After XP, I missed the ability to use my dial out program,. It's always something. Anyway, QB64, post 2010, became so much faster and better. I'd say the few losses of the past are narly forgotten. By the way, my Win98 had its wake about three years ago. Were yo the one who sent flowers? That was a nice touch. I should have taken the time to dry them, but I was too busy using up the bag of fertilizer Steve sent me... that, and buying new shoes, which I needed after burning them on Steve's gift when it arrived on my front porch.

Pete