Help needed to validate if a directory exists and the user has write permissions

49 ビュー (過去 30 日間)
Jason Newman
Jason Newman 2019 年 3 月 8 日
コメント済み: Guillaume 2019 年 3 月 11 日
In our scripts, we often ask a user to give diretory or path. That path may contain files that need to be processed and results may need to be written to that path. So a check is needed to verify that the path exists and that the user has write access to that path. In other cases, we might need to check only read access, but the idea is the same.
So I have used a while loop to see if the given path exists. If it doesn't, the user is asked a question again. I have another while loop to check for write access. Here is where I run into problem. I tried using a nested while loop to evaluate both conditions, but if the user ends a valid path that they do not have write access to, the program runs in and endless loop. I tried separating both checks, but I get the same problem. The only solution that I have come up with is to ask the user to check the permissions and run the script if the write access check fails (hence the return command near the end of the code). Ideally, I'd like the original question to be asked again (give a valid path with write access) rather than halting the program all together. Is there anyway to do that?
I have pasted sample code below as proof of concept. I realize that it is probably ideal to have input checks in a function, but I'd like to see if my problem can be fixed first.
% evaluate if folder exits and user has write access to that folder
clear %include this only for debugging or if variables need to be cleared
clc %include this only for debugging
direxists=0; %variable to set direxits flag
while direxists==0
dir=input('Please enter a directory or path:','s');
if exist (dir, 'dir')
[status,values]=fileattrib(dir); %this creates a structure array called values with the attribues of the input path
disp(' ');
disp('Congrats your directory exists');
writeaccess=values.UserWrite; %this extracts the UserWrite attribute from the values structure array
%so it can be evaluated in the next while loop.
direxists=1;
else
disp('That directory or path does not exist. Please try again');
statuscheck=0; % directory did not exist, so the question is asked again
end %ends the if statement
end %ends the while loop to check to see if directory exists.
writeaccesscheck=0; %sets the writeaccesscheck flag to 0
while writeaccesscheck==0
if writeaccess==1
disp(' ');
disp('Congrats. You have write access to the directory');
disp(' ');
disp('Now that we have verified that the diretory exits and you have write access, press any key to contine');
pause
writeaccesscheck=1; %writeacess has been verified. Loop will exit and program proceeds once a user hits a key
else
disp('You do not have write access. Please pick a directory where you have write access and run this script again.');
return %this will halt the program to keep this loop from running over and over if writeaccess=0
writeaccesscheck=0; %The user did not have write access.
end %ends the write access check if loop
end %ends the while loop to check write access
disp(' ');
disp('Good Job. That directory exits and you can write files to there.');

採用された回答

Jan
Jan 2019 年 3 月 8 日
編集済み: Jan 2019 年 3 月 8 日
What about this:
function Folder = ChooseWritableFolder(Base)
ready = false;
while ~ready
Folder = uigetdir(Base, 'Select a folder');
if ~ischar(Folder) % User pressed: Cancel
return; % Or error(), as you want
end
[status, Attrib] = fileattrib(Folder);
ready = any(Attrib.UserWrite);
if ~ready
fprintf(2, 'Folder is write protected: %s\n', Folder);
pause(2);
% Or:
% msgbox({'Folder is write protected:', Folder, ...
% 'Select a writable folder instead.'}, ...
% 'Folder selection, 'warn', 'modal');
Base = fileparts(Folder); % Start next search in the parent folder
end
end
Call this like:
Folder = ChooseWritabelFolder(cd);
if ~ischar(Folder)
error('User cancelled folder selection');
end
  2 件のコメント
Jason Newman
Jason Newman 2019 年 3 月 11 日
I'll have to check to see later how thats works. I ended up answering my own question to an extent with another project... using two functions (one to check to see if dir exits, and then verify read/write) seems to solve the issue.
Jason Newman
Jason Newman 2019 年 3 月 11 日
This looks good, though I may approach it somewhat differently at the end. Always more than one way to do something....

サインインしてコメントする。

その他の回答 (2 件)

Geoff Hayes
Geoff Hayes 2019 年 3 月 8 日
編集済み: Geoff Hayes 2019 年 3 月 8 日
Jason - why do you need a second while loop to check the write permissions of the folder? You could just use the if/else you have above and if there is no write access to the folder, then just exit the loop (if you don't want to prompt the user for a new directory). A simplified version of the above (using uigetdir) could be
selectedDir = uigetdir;
while ~isnumeric(selectedDir)
[status, values] = fileattrib(selectedDir);
if values.UserWrite == 1
fprintf('Congrats. You have write access to the directory\n');
break;
else
fprintf('You do not have write access. Please pick a directory where you have write access.\n');
selectedDir = uigetdir;
end
end
If, when the dialog is open, and the user presses the cancel button or closes the dialog, then selectedDir is zero. When this happens, we don't bother trying to validate the path and assume that the user is not interested in choosing a folder.
  1 件のコメント
Jason Newman
Jason Newman 2019 年 3 月 11 日
This seems to work as well. I guess I can't accept more than one answer even though there are multiple ways of doing things.

サインインしてコメントする。


Guillaume
Guillaume 2019 年 3 月 8 日
It's rarely a good idea to perform this kind of checks. The situation may have changed in between the moment you did the check and the moment you actually perform the read/write (e.g. permission change). For all you know, at the moment you perform the read or write, the path may actually point to a completely different location to the one you originally checked. (e.g. thumb drive was removed and replaced by another one)
With file IO, it's actually more reliable to be reactive. Try the file write and if it fails, report the cause to the user. It's unfortunate that matlab IO is still stuck in last century, it would be much better if it implemented exceptions.
In any case, writing a reliable file write test is difficult in pure matlab, at least for NTFS type file systems where the permissions can be very fine grain (you may be able to write to an existing file, but not create a new one).
  2 件のコメント
Jason Newman
Jason Newman 2019 年 3 月 11 日
Maybe I can think of trying something like that (e.g., write test) in version 2.0. There are all sorts of things that could happen, but only a subset of the most common can be covered. At some point, people have to read instructions.
Guillaume
Guillaume 2019 年 3 月 11 日
You will have more reliable code, and more importantly, simpler in the long run, if you react to IO errors rather than try to preempt them. As I said, it's pointless to check if an IO is going to succeed beforehand, the situation may be completely different by the time you actually perform the IO for a variety of reasons which may not be under the user control.
It's unfortunate that matlab IO is so outdated, you don't get consistent exceptions when IO fail so you have to parse system dependent error messages. In your case, I'd do something like this:
%code before IO. Performs no check for access. It's pointless.
try %wrap IO in try catch
[fid, err] = fopen(somefile);
if fid == -1 %fopen fail for any reason, maybe no permission, maybe something else
throw MException('something:IOError', err);
end
fwrite(fid, something); %can error for all kind of reasons, no write access, disk full, etc. all of which will be caught by
%...
catch ME
%if we get here IO fail. Parse ME.exception to find the cause or just report to the user
fprintf('Could not write %s because %s', somefile, ME.exception);
end

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeFile Operations についてさらに検索

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by