0

I have a Windows batch script that operates in one of three modes, depending on the number of command-line arguments:

1 command-line argument: plays named .wav file

2 command-line arguments: runs command specified by first command-line argument and then plays .wav file specified via second command-line argument

3 command-line arguments: runs command specified by first command-line argument and then plays .wav file specified via second or third command-line argument, depending on whether the command was successful

My issue is with the third mode: The script always plays the first sound, regardless of whether the command was successful. Any suggestions will be appreciated.

@ECHO OFF
Setlocal disableDelayedExpansion

:: OVERVIEW

:: This script optionally runs a command and then uses Eli Fulkerson's
:: `sounder.exe` to play the specified or default sound.

:: The Media environment variable must be defined and contain the path of a
:: folder containing one or more .wav files.


:: REFERENCE

:: https://www.elifulkerson.com/projects/commandline-wav-player.php


:: USAGE CASE 1: NO ARGUMENTS

:: If the script is invoked without arguments, it plays the default sound file
:: `beep-05.wav`.


:: USAGE CASE 2: ONE ARGUMENT

:: The single argument is the name of a sound file, minus the .wav extension.
:: sound.bat will play this sound file.  Example:

::    sound meow1


:: USAGE CASE 3: TWO ARGUMENTS

:: The first argument contains a command to be run; the second argument is the
:: name of a sound file, minus the .wav extension.  sound.bat will play the
:: sound after the command finishes running, regardless of exit code.  Example:

::    sound "dir C:\Phillip" meow1


:: USAGE CASE 4: THREE ARGUMENTS

:: The first argument contains a command to be run; the second and third
:: arguments name sound files, minus the .wav extension to be played if the
:: executes successfully or fails, respectively.

::    sound "dir C:\Phillip" meow1 meow2


:: NOTE

:: It is not currently possible to use this scripts to play sound files that are
:: located outside the Media folder.


:: AUTHOR

:: Phillip M. Feldman


:: REVISION HISTORY

:: June 5, 2020, Phillip M. Feldman: Initial version.


If not defined Media (
   msg "%username%" The Media environment variable is not defined!
   GOTO :EOF
)

If not EXIST %Media% (
   msg "%username%" The Media folder '%Media%' does not exist!
   GOTO :EOF
)


If '%1' == '' (

   :: No command-line arguments.
   sounder %Media%\beep-05.wav
   GOTO :EOF
)

If '%2' == '' (

   :: One command-line argument.
   sounder %Media%\%1%.wav
   GOTO :EOF
)

If '%3' == '' (

   :: Two command-line arguments.
   %~1
   sounder %Media%\%2%.wav
   GOTO :EOF
)

If '%4' == '' (

   :: Three command-line arguments.
   %~1

   If %ERRORLEVEL% == 0 (
      sounder %Media%\%2%.wav
   ) else (
      sounder %Media%\%3%.wav
   )
   GOTO :EOF
)
Phillip M. Feldman
  • 468
  • 1
  • 7
  • 17
  • 1
    `If '%3' == ''` Is the wrong syntax. Single quotes do not provide the protections double quotes do, and in case the filepath argument contains spaces and has been passed to the script doublequoted, `~` modifier should be used `If "%~3" == ""` Additionally, you are expanding your parameter variables incorrectly. `sounder %Media%\%3%.wav` should be: `sounder "%Media%\%~3.wav"` – T3RR0R Jun 05 '20 at 15:28
  • 2
    And please remove all of those malformaed labels, `:: ` and replace them with the proper commenting command `Rem `. _Or better, remove them all as we only need a [MCVE], we don't need all of that_. Additionally `If not EXIST %Media% (` does not determine whether a `folder` named `ExpandedValueOfMediaVariable` `exist`s. – Compo Jun 05 '20 at 15:31
  • 1
    You need delayed expansion for the `errorlevel` variable or alternatively use `if not errorlevel 1` – Stephan Jun 05 '20 at 16:19

3 Answers3

1

An alternative to this:

If "%~4" == "" (

   :: Three command-line arguments.
   %~1

   If "%ERRORLEVEL%" == "0" (
      sounder "%Media%\%~2.wav"
   ) else (
      sounder "%Media%\%~3.wav"
   )
   GOTO :EOF
)

Is to use && (On success) and || (On fail) conditional operators:

(If Not "%~3" == "" %~1) && sounder "%Media%\%~2.wav" || sounder "%Media%\%~3.wav"
T3RR0R
  • 2,747
  • 3
  • 10
  • 25
  • You can't use `::` inside of a code block because it's technically a label. – SomethingDark Jun 05 '20 at 16:38
  • ``` (If Not "%~3" == "" %~1) && sounder "%Media%\%~2.wav" || sounder "%Media%\%~3.wav" ``` is very compact, but when the command executes successfully, both sounds are playing. – Phillip M. Feldman Jun 06 '20 at 16:12
  • Re. single quotes vs. double quotes: If the command contains blanks, it must be double quoted, and parsing fails in this case if the code uses double quotes. – Phillip M. Feldman Jun 06 '20 at 16:16
  • Well, @PhillipM.Feldman, it won't always fail, but in some cases you need [this](https://stackoverflow.com/a/9871083/12861751) – ScriptKidd Jun 07 '20 at 06:57
0

With two minor changes, the script (see below) now works. I want to thank everyone who helped or tried to help.

@ECHO OFF
Setlocal enableDelayedExpansion

:: OVERVIEW

:: This script optionally runs a command and then uses Eli Fulkerson's
:: `sounder.exe` to play the specified or default sound.

:: The Media environment variable must be defined and contain the path of a
:: folder containing one or more .wav files.


:: REFERENCE

:: https://www.elifulkerson.com/projects/commandline-wav-player.php


:: USAGE CASE 1: NO ARGUMENTS

:: If the script is invoked without arguments, it plays the default sound file
:: `beep-05.wav`.


:: USAGE CASE 2: ONE ARGUMENT

:: The single argument is the name of a sound file, minus the .wav extension.
:: sound.bat will play this sound file.  Example:

::    sound meow1


:: USAGE CASE 3: TWO ARGUMENTS

:: The first argument contains a command to be run; the second argument is the
:: name of a sound file, minus the .wav extension.  sound.bat will play the
:: sound after the command finishes running, regardless of exit code.  Example:

::    sound "dir C:\Phillip" meow1


:: USAGE CASE 4: THREE ARGUMENTS

:: The first argument contains a command to be run; the second and third
:: arguments name sound files, minus the .wav extension to be played if the
:: executes successfully or fails, respectively.

::    sound "dir C:\Phillip" meow1 meow2


:: NOTE

:: It is not currently possible to use this scripts to play sound files that are
:: located outside the Media folder.


:: AUTHOR

:: Phillip M. Feldman


:: REVISION HISTORY

:: June 5, 2020, Phillip M. Feldman: Initial version.


If not defined Media (
   msg "%username%" The Media environment variable is not defined!
   GOTO :EOF
)

If not EXIST %Media% (
   msg "%username%" The Media folder '%Media%' does not exist!
   GOTO :EOF
)


If '%1' == '' (

   :: No command-line arguments.
   sounder %Media%\beep-05.wav
   GOTO :EOF
)

If '%2' == '' (

   :: One command-line argument.
   sounder %Media%\%1%.wav
   GOTO :EOF
)

If '%3' == '' (

   :: Two command-line arguments.
   %~1
   sounder %Media%\%2%.wav
   GOTO :EOF
)

If '%4' == '' (

   :: Three command-line arguments.
   %~1

   If ERRORLEVEL 1 (
      sounder %Media%\%3%.wav
   ) else (
      sounder %Media%\%2%.wav
   )
   GOTO :EOF
)
Phillip M. Feldman
  • 468
  • 1
  • 7
  • 17
  • It looks like you're after something like this: `If Not "%~3"=="" (%~1 2>NUL && (sounder "%Media%\%~2.wav") || sounder "%Media%\%~3.wav") Else If Not "%~2"=="" (%~1 2>NUL & sounder "%Media%\%~2.wav") Else If Not "%~1"=="" (sounder "%Media%\%~1.wav") Else sounder "%Media%\beep-05.wav"`. However, to make your script anywhere close to being useful in a real world scenario, you should first perform some validation of the input, to verify whether the entered sound files exist, informing the end user of errors before attempting to play any sounds or run any entered commands. – Compo Jun 07 '20 at 13:40
  • Validating the command sounds like a good idea, but I have no idea how to do this. – Phillip M. Feldman Jun 08 '20 at 06:47
  • I wasn't thinking of validating the command, just the existence of any wav file entered as an argument. To validate a command, i.e. to check it is really a command, the only way to reliably do that is to run it, and see what happens. If you're running it as part of validation, then you'd end up having to run every command twice, which seems very wasteful. Is there a list of commands which are allowed, or is you intention to be able to use any internal command or external executable? _(With a list you have a chance, with any you've got none)_. – Compo Jun 08 '20 at 08:26
0

Here's an example using the method I proposed via comment under your own answer, and with some additional validation included:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion

Rem OVERVIEW
Rem This script optionally runs a command and then uses Eli Fulkerson's
Rem `sounder.exe` to play the specified or default sound.
Rem The Media environment variable must be defined and contain the path of a
Rem folder containing one or more .wav files.

Rem REFERENCE
Rem https://www.elifulkerson.com/projects/commandline-wav-player.php

Rem USAGE CASE 1: NO ARGUMENTS
Rem If the script is invoked without arguments, it plays the default sound file
Rem `beep-05.wav`.  Example:
Rem    sound

Rem USAGE CASE 2: ONE ARGUMENT
Rem The single argument is the name of a sound file, minus the .wav extension.
Rem sound.bat will play this sound file.  Example:
Rem    sound meow1

Rem USAGE CASE 3: TWO ARGUMENTS
Rem The first argument contains a command to be run; the second argument is the
Rem name of a sound file, minus the .wav extension. The sound will play upon
Rem completion of the command, regardless of its returned exit code.  Example:
Rem    sound "dir C:\Phillip" meow1

Rem USAGE CASE 4: THREE ARGUMENTS
Rem The first argument contains a command to be run; the second and third
Rem arguments name sound files, minus their .wav extensions, the first to be
Rem played if the command executes successfully and the second if it fails.
Rem Example:
Rem    sound "dir C:\Phillip" meow1 meow2

Rem NOTE
Rem It is not currently possible to use this script to play sound files which
Rem are located outside the Media folder.

Rem AUTHORS
Rem Phillip M. Feldman, Compo

Rem REVISION HISTORY
Rem June 8 2020, Compo: Improved version.
Rem June 5 2020, Phillip M. Feldman: Initial version.

Rem Validates the environment and input of no more than three arguments.
Set "$=0"
%__AppDir__%where.exe /Q sounder.exe && (If Not Defined Media (Set "$=16"
        %__AppDir__%msg.exe "%UserName%" The Media environment variable is not defined.
    ) Else For %%G In ("%Media%") Do If "%%~aG" Lss "d" (Set "$=8"
        %__AppDir__%msg.exe "%UserName%" The Media folder '%Media%' does not exist!
    ) Else %__AppDir__%where.exe /Q "%Media%":"*.wav" && (If Not "%~4"=="" (Set "$=2"
            %__AppDir__%msg.exe "%UserName%" Unexpected number of arguments.)) || (Set "$=4"
        %__AppDir__%msg.exe "%UserName%" There are no WAV files in '%Media%'.)) || (Set "$=32"
    %__AppDir__%msg.exe "%UserName%" Eli Fulkerson's sounder.exe was not found.)

If %$% NEq 0 Exit /B %$%

Rem Validates that the input .wav file names exist in the Media directory.
If Not "%~3"=="" (%__AppDir__%where.exe /Q "%Media%":"%~2.wav" || (Set "$=1"
        %__AppDir__%msg.exe "%UserName%" Sound file '%Media%\%~2' does not exist.)
    %__AppDir__%where.exe /Q "%Media%":"%~3.wav" || (Set "$=1"
        %__AppDir__%msg.exe "%UserName%" Sound file '%Media%\%~3' does not exist.)
) Else If Not "%~2"=="" (%__AppDir__%where.exe /Q "%Media%":"%~2.wav" || (Set "$=1"
        %__AppDir__%msg.exe "%UserName%" Sound file '%Media%\%~2' does not exist.)
) Else If Not "%~1"=="" (%__AppDir__%where.exe /Q "%Media%":"%~1.wav" || (Set "$=1"
        %__AppDir__%msg.exe "%UserName%" Sound file '%Media%\%~1' does not exist.)
) Else %__AppDir__%where.exe /Q "%Media%":"beep-05.wav" || (Set "$=1"
    %__AppDir__%msg.exe "%UserName%" Sound file '%Media%\beep-05.wav' does not exist.)

If %$% NEq 0 Exit /B %$%
Set "$="

Rem The commands to run if validation is successful.
If Not "%~3"=="" (%~1 2>NUL && (sounder.exe "%Media%\%~2.wav") || sounder.exe "%Media%\%~3.wav"
) Else If Not "%~2"=="" (%~1 2>NUL
    sounder.exe "%Media%\%~2.wav") Else If Not "%~1"=="" (sounder.exe "%Media%\%~1.wav"
) Else sounder.exe "%Media%\beep-05.wav"

GoTo :EOF
Compo
  • 36,585
  • 5
  • 27
  • 39