Jump to content

_FileInPath( filename ) UDF


Recommended Posts

I did a quick search for it on the forums, but maybe I missed it, so I made my own.

It searches the PATH environment variable

Example:

ConsoleWrite(_FileInPath("robocopy.exe") & @CRLF)

UDF:

; #FUNCTION# ====================================================================================================================
; Name...........: _FileInPath
; Description ...: Returns the location in the PATH environment variable containing a file
; Syntax.........: _FileInPath( $sFilename )
; Parameters ....: $sFilename - The file to search for (filename only, no folders)
; Return values .: Success - Returns the path to the file and set @error=0 (@extended=1 a specified path and the file is there)
;                  Failure - Returns "" and sets @error:
;                  |1 - File not found in @WorkingDir or the PATH (@extended=1 if PATH is blank)
;                  |2 - Could not StringSplit the PATH
; Author ........: Gigglestick (c0deWorm)
; Modified.......: Comments added and logic modified
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _FileInPath($asFilename)
    If StringInStr($asFilename, "\") Then
        If FileExists($asFilename) Then
            ; if a full or relative path was specified and the file exists then return the path
            Return SetError(0, 1, $asFilename)
        Else
            ; if a path was specified and the file does not exist then return "file not found" error
            Return SetError(1, 0, "")
        EndIf
    EndIf
    ; if the file is found in the current working directory then return the absolute file path
    If FileExists(@WorkingDir & "\" & $asFilename) Then Return SetError(0, 0, @WorkingDir & "\" & $asFilename)
    Local $i, $lasPath, $lsPath = EnvGet("PATH")
    ; if variable containing PATH is empty then return "file not found" error and @extended=2
    If StringLen($lsPath) = 0 Then Return SetError(1, 1, "")
    ; if PATH is set but only has one entry then check it
    If StringLen($lsPath) > 0 And Not StringInStr($lsPath, ";") Then
        If FileExists($lsPath & "\" & $asFilename) Then
            ; if the filename is found in the single PATH entry then return the file path
            Return SetError(0, 0, $lsPath & "\" & $asFilename)
        Else
            ; if PATH is a single entry and does not contain the filename then return "file not found" error
            Return SetError(1, 0, "")
        EndIf
    EndIf
    ; remove any blank entries (double semicolons)
    While StringInStr($lsPath, ";;")
        $lsPath = StringReplace($lsPath, ";;", ";")
    WEnd
    ; split variable containing PATH into an array of single paths
    $lasPath = StringSplit($lsPath, ";")
    ; if ";" not found in split then return error
    If @error Then Return SetError(2, 0, "")
    ; loop through array of single paths, searching for absolute file path and return absolute file path if found
    For $i = 1 To $lasPath[0]
        If FileExists($lasPath[$i] & "\" & $asFilename) Then Return SetError(0, 0, $lasPath[$i] & "\" & $asFilename)
    Next
    ; unable to find filename in current working directory or PATH so return error
    Return SetError(1, 0, "")
EndFunc   ;==>_FileInPath

Edit - May 10: Evaluated MHz's comments and revised the code. It should work much better now.

  • If a full or relative path is specified and the file exists there then return that path, or @error=1 (file not found)
  • If the file exists in the @WorkingDir then return that path
  • If the PATH is empty then return @error=1 and @extended=1
  • If the PATH contains only a single entry, and it contains the file then return that path, or @error=1 (file not found)
  • Clear blank entries (consecutive semicolons) from the PATH
  • Split the path and check each entry for the file, returning the path if found
  • If none of the above works then @error=1 (file not found)
Edited by Gigglestick

My UDFs: ExitCodes

Link to comment
Share on other sites

Regarding @WorkingDir = @ScriptDir can be false to presume. FileChangeDir can change @WorkingDir which I agree with. Inheritance of a working directory on initial execution can make @WorkingDir other then @ScriptDir.

If I start a executable from a command prompt. Note the working directory of the prompt is shown as C:\

C:\> C:\temp\script.exe

The inherited working directory would be C:\ and not C:\temp. Also using the Start command allows change of working directory for the executable to inherit.

Using the Run box can set the working directory to your user profile, the last time I checked.

Using the registry can set the working directory to the system drive, the last time I checked.

Group Policy and other means of execution may affect the inheritance of working directory so you should not presume that @WorkingDir will equal @ScriptDir at the time of initial execution.

Edit:

@JohnOne

The test of FileExists on $asFilename can return true if $asFilename does exist in @workingDir and not exist not in @ScriptDir. @ScriptDir is an absolute folder path which can be an issue when you use a relative path (just a filename) such as $asFilename uses.

Edited by MHz
Link to comment
Share on other sites

Thanks for the explanaition, I was under the impression that FileExists() only checked the folder which the process ran from if no path is given, and in the case of "file.ext" would only be @ScriptDir.

If I'm honest, I dont understand what @WorkingDir even means.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

You may need to understand a working directory to make good use of Gigglesticks fine UDF. :unsure:

You use working directories all the time without knowing it. So perhaps you may understand the explanation below.

You execute a file and it becomes a process in ram memory. The process gets to inherit a working directory to focus on. The working directory is the view, or what directory that the process can see.

Now try to imagine that you are a process, not in ram memory but as an external process working with files in Explorer. Have an Explorer window open in front of you and set the view pane of Explorer to C:\. You as an imaginary process can now see files in the directory of C:\. You can work with those files in C:\ but you cannot see files in other directories because Explorer can only show one directory at a time in the view pane. Now change the working directory of Explorer by setting the view pane to C:\Windows. You as an imaginary process can now see files in the directory of C:\Windows. You can work with those files in C:\Windows but you cannot see files in other directories because Explorer can only show one directory at a time in the view pane. You can keep changing directories in Explorer as you like and note that you are basically changing the working directory each time you set a new path in Explorer and you also get a view that is the same as the working directory.

The description above is similar to how a process in ram memory works. The working directory can change and thus the view of the files that the process sees is relative to the working directory. If the working directory is @ScriptDir, then the process can see the physical form of itself and other files in the @ScriptDir. If the working directory alias @WorkingDir is some other directory then it can see the files in that directory and not its physical form of itself and other files in the @ScriptDir.

Link to comment
Share on other sites

So if the working Dir can be different from that which the process is ran from, could not the function above falsely return true when the given file is in @ScriptDir and not "PATH", but @WorkingDir is in "PATH" ?

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

JohnOne,

I can not see how it would be possible for the UDF to return a file path if it does not exist in @WorkingDir or PATH. You mentioned previous in post #2 about a concern about the line below.

If FileExists($asFilename) Then Return SetError(0, 0, @WorkingDir & "\" & $asFilename)

Using FileExists on a just a filename looks for the filename in @WorkingDir. So the return of @WorkingDir & "\" & $asFilename would be correct. Use of for example FileExists($asFilename) and FileExists(@WorkingDir & "\" & $asFilename) should be either both True or both false with the same conditions as they both reference the file in @workingDir. Returning @ScriptDir & "\" & $asFilename as you suggested would be incorrect if @WorkingDir is different to @ScriptDir.

This is an test script and I commented the UDF code for the review.

; *** test _FileInPath() ***
; look for @ScriptName in @ScriptDir
FileChangeDir(@ScriptDir)
$absolute_filepath = _FileInPath(@ScriptName)
_GetError(@error, @extended, $absolute_filepath, 'Find ' & @ScriptName)

;  look for @ScriptName in @WindowsDir
FileChangeDir(@WindowsDir)
$absolute_filepath = _FileInPath(@ScriptName)
_GetError(@error, @extended, $absolute_filepath, 'Find ' & @ScriptName)

; change PATH to show issue with missing ";"
EnvSet('PATH', @WindowsDir)
FileChangeDir(@WindowsDir)
$absolute_filepath = _FileInPath(@ScriptName)
_GetError(@error, @extended, $absolute_filepath, 'Find ' & @ScriptName & ' with missing ";" in PATH')

; change PATH to show issue empty path while working directory is valid
EnvSet('PATH', '')
FileChangeDir(@ScriptDir)
$absolute_filepath = _FileInPath(@ScriptName)
_GetError(@error, @extended, $absolute_filepath, 'Find ' & @ScriptName & ' with empty PATH')

; *** UDF by Gigglestick (c0deWorm) showing possible issues to consider ***
; working directory could be seached before returning error on an empty variable containing PATH
;     i.e. PATH is secondary compared to the working directory I usually notice
; should a variable containing PATH be be regarded as an error if ";" is not found?
;     i.e. a single path without a ";" would IMO be considered valid but would return error in the UDF
;
; *** UDF by Gigglestick (c0deWorm) with added comments ***
; #FUNCTION# ====================================================================================================================
; Name...........: _FileInPath
; Description ...: Returns the location in the PATH environment variable containing a file
; Syntax.........: _FileInPath( $sFilename )
; Parameters ....: $sFilename - The file to search for (filename only, no folders)
; Return values .: Success - Returns the path to the file and @error=0
;                  Failure - Returns "" and sets @error:
;                  |1 - File not found anywhere in the PATH
;                  |2 - Cannot read the PATH environment variable
;                  |3 - Filename contains a backslash
;                  |4 - The PATH environment variable could not be StringSplit
; Author ........: Gigglestick (c0deWorm)
; Modified.......: Comments added
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func _FileInPath($asFilename)
    Local $i, $lasPath, $lsPath = EnvGet("PATH")
    ; if variable containing PATH is empty, then return error
    If $lsPath = "" Then Return SetError(2, 0, "")
    ; if the filename given contains a backslash then return error
    If StringInStr($asFilename, "\") Then Return SetError(3, 0, "")
    ; if the file is found in the current working directory, then return the absolute file path
    If FileExists($asFilename) Then Return SetError(0, 0, @WorkingDir & "\" & $asFilename)
    ; split variable containing PATH into an array of single paths
    $lasPath = StringSplit($lsPath, ";")
    ; if ";" no found in split then return error
    If @error Then Return SetError(4, 0, "")
    ; loop through array of single paths, searching for absolute file path and return absolute file path if found
    For $i = 1 To $lasPath[0]
        If FileExists($lasPath[$i] & "\" & $asFilename) Then Return SetError(0, 0, $lasPath[$i] & "\" & $asFilename)
    Next
    ; unable to find filename in current working directory or PATH so return error
    Return SetError(1, 0, "")
EndFunc   ;==>_FileInPath

Func _GetError($code, $extended, $return_value, $comment = '')
    ; show various conditions and results
    MsgBox(0x40000, '_GetError()', _
            'Comment: ' & $comment & @CRLF & _
            '@error: ' & $code & @CRLF & _
            '@extended: ' & $extended & @CRLF & _
            'Return: ' & $return_value & @CRLF & _
            '@WorkingDir: ' & @WorkingDir & @CRLF & _
            '@ScriptDir: ' & @ScriptDir & @CRLF & _
            '@WorkingDir = @ScriptDir: ' & (@WorkingDir = @ScriptDir) )
EndFunc

Gigglestick,

I list below some possible issues I found with the UDF.

  • Working directory could be seached before returning error on an empty variable containing PATH

    i.e. PATH is secondary compared to the working directory I usually notice

  • Should a variable containing PATH be be regarded as an error if ";" is not found?

    i.e. a single path without a ";" would IMO be considered valid but would return error in the UDF

The test script above shows the possible issues in the last 2 calls to the UDF. Something for you to review.
Link to comment
Share on other sites

@MHz - Thanks for testing those scenarios. I clearly hadn't checked all the logic. I think it'll work much better now. The original post has been updated.

@JohnOne

Maybe this will help:

Script: C:\Myfiles\Scripts\MyScript.exe

File: C:\Myfiles\Scripts\Data.txt

PATH: c:\windows\system32;c:\windows

@WorkingDir when the script starts is C:\Myfiles\Scripts, so if we _FileInPath("Data.txt") it'll check C:\Myfiles\Scripts and find the file.

If you FileChangeDir("C:\") then _FileInPath("Data.txt") again, it'll check C:\ (@WorkingDir, not found), c:\windows\system32 (not found), and c:\windows (not found). So even though the file is in @ScriptDir, we're only looking in the "current" folder and the PATH.

This is how other things in Windows work. If you hit Start and Run, and type "cmd" it's looking in the working directory, which is your HOMEPATH (another environment variable), then it'll check each directory listed in the PATH. Unless you have changed it, C:\Windows\System32 is in the PATH by default, so it finds cmd.exe there and runs it. When it starts cmd.exe, it starts it in your HOMEPATH instead of the System32 directory because it's more likely that things relevant to you will be on your home drive than under the System32 directory. From cmd.exe's perspective its own @ScripDir is C:\Windows\System32, but its @WorkingDir is your HOMEPATH.

Edited by Gigglestick

My UDFs: ExitCodes

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...