Skip to content

Instantly share code, notes, and snippets.

@kouichi-c-nakamura
Created July 7, 2016 03:37
Show Gist options
  • Save kouichi-c-nakamura/4a8674bfa3e2b5de96930c7ad2017ce8 to your computer and use it in GitHub Desktop.
Save kouichi-c-nakamura/4a8674bfa3e2b5de96930c7ad2017ce8 to your computer and use it in GitHub Desktop.
Macに保存していたデータをWindowsマシンへ移行する時の苦労話 ref: http://qiita.com/kouichi-c-nakamura/items/518cd401e35889646b2e
csrutil disable
csrutil enable
ditto -v sourcefolder destinationfolder
* ===> x
: ===> -
? ===> _
" ===> '
< ===> [
> ===> ]
| ===> _
function [report,failures] = correctWinIncompatibleFileNames(parentFolder,varargin)
% [report,failures] = correctWinIncompatibleFileNames(parentFolder)
% [report,failures] = correctWinIncompatibleFileNames(parentFolder,dorename)
%
% correctWinIncompatibleFileNames runs recursively across folder hierarchy
% to correct invalid file names in Windows OS. You can use this only on Mac
% platform though.
%
% * ===> x
% : ===> -
% ? ===> _
% " ===> '
% < ===> [
% > ===> ]
% | ===> _
%
% INPUT ARGUMENTS
% parentFolder A valid foder path
%
% dorename true (default) | false
% If you choose false, correctWinIncompatibleFileNames does
% not run Terminal's mv command, but only returns report
% table for you to simulate the outcome.
%
% OUTPUT ARGUMENT
% report A table output
% Variables
% Folder
% OriginalNames
% NewNames
% Illegals
% Bytes
% Date
%
% failures Similar to report but for failures
%
%
%% Filter for WinMerge
%
% ## This is a directory/file filter template for WinMerge
% name: Ignore Asterisk and Colon
% desc: Ignore files whose names include asterisk (*) and Colon (:)
%
% ## Select if filter is inclusive or exclusive
% ## Inclusive (loose) filter lets through all items not matching rules
% ## Exclusive filter lets through only items that match to rule
% ## include or exclude
% def: include
%
% ## Filters for filenames begin with f:
% ## Filters for directories begin with d:
% ## (Inline comments begin with " ##" and extend to the end of the line)
%
% f: \w+\*\w*\.{0,1}\w* ## Filter for filename
% f: \:
% f: \?
%
% ##d: \\subdir$ ## Filter for directory
%
%
%
% See also
% unix, movefile, dir, preallocatestruct, regexptranslate,
% correctWinIncompatibleFileNames_test,
% correctWinIncompatibleFileNames_fixture
%
% https://msdn.microsoft.com/en-us/library/aa365247
p = inputParser;
p.addRequired('parentFolder',@(x) isdir(x));
p.addOptional('dorename',true,@(x) isscalar(x) && x == 0 || x == 1);
p.parse(parentFolder,varargin{:});
dorename = p.Results.dorename;
assert(isdir(parentFolder),...
'correctFileNamesOnWindows:parentFolder:invalid',...
'folder %s is invalid for a folder',parentFolder);
cs = repmat({''},1e5,1); % up to 100000 illegal files at one time
cc = cell(1e5,1);
nan = NaN(1e5,1);
report = table(cs,cs,cs,cc,nan,cs);
report.Properties.VariableNames = {'Folder','OriginalNames','NewNames','Illegals','Bytes','Date'};
failures = table(cs,cs,cs,cc,nan,cs,cs);
failures.Properties.VariableNames = [report.Properties.VariableNames,{'Cmdout'}];
clear cs c
row = 0;
row2 = 0; % for failures
[report,row,failures,row2] = doOneFolder(parentFolder,report,row,dorename,failures,row2);
report(row+1:end,:) = [];
failures(row2+1:end,:) = [];
if ~dorename
warning('correctWinIncompatibleFileNames:dorename:false',...
'No file name has been changed by correctWinIncompatibleFileNames.')
end
end
%--------------------------------------------------------------------------
function [report,row,failures,row2] = doOneFolder(thisFolder,report,row,dorename,failures,row2)
% report is a table
list = dir(thisFolder);
names = {list(:).name}';
% if there is a folder
n = length(names);
for i = 1:n
if strcmp(list(i).name,'.')
continue
elseif strcmp(list(i).name,'..')
continue
end
if list(i).isdir
[report,row,failures,row2] = doOneFolder(fullfile(thisFolder,list(i).name),...
report,row,dorename,failures,row2);
else
origName = list(i).name;
NewNames = origName;
illegal_count = 0;
illegals = preallocatestruct({'char','newchar','startIndex'},[64,1]);
% correct illegal file names
% https://msdn.microsoft.com/en-us/library/aa365247
[illegals,illegal_count,NewNames] = findillegals('*','x',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals(':','-',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals('?','_',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals('"','\''',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals('<','[',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals('>',']',...
NewNames,illegals,illegal_count);
[illegals,illegal_count,NewNames] = findillegals('|','_',...
NewNames,illegals,illegal_count);
%% KEEP THESE EXAMPLES
%
% movefile('abc"de.rtf','abc''de.rtf'); % works on Mac
%
% unix('mv abc\"de\.rtf abcde.rtf'); % works on Mac
%
% unix('mv abc\"de.rtf abcde.rtf'); % works on Mac
%
% unix('mv abc"de.rtf abcde.rtf'); % does not work on Mac
% % /bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
% % /bin/bash: -c: line 1: syntax error: unexpected end of file
%
% unix('mv "abc\"de.rtf" abcde.rtf'); % works on Mac
%
%
%
% unix('mv abc\"de.rtf "abc''de.rtf"'); % works on Mac
%
% unix('mv abc\"de.rtf abc\''de.rtf'); % works on Mac
%
%
%
% movefile('abc\*de.rtf','abcxde.rtf'); % does not work on Mac
%
% unix('mv abc\*de\.rtf abcxde\.rtf'); % works on Mac
%
illegals(illegal_count+1:end) = []; % up to 64 illegals
if illegal_count > 0
% sort illegals
startInd = [illegals(:).startIndex];
[~,ind] = sort(startInd);
illegals = illegals(ind);
if ismac
if dorename
% escape2 = @(x) regexprep(x,'"','\\"'); % need to escape "
% escape = @(x) regexprep(regexptranslate('escape',fullfile(thisFolder,x)),'\s','\\ ');
% %NOTE need to escape a space with \ even after regexptranslate
%
% src = escape2(escape(origName));
% dst = escape2(escape(NewNames));
src = fullfile(thisFolder,origName);
dst = fullfile(thisFolder,NewNames);
src = findQinpath(src);
dst = findQinpath(dst);
cmd = sprintf('mv -f %s %s',src,dst);
[status,cmdout] = unix(cmd,'-echo');
%NOTE movefile won't work properly with \*
% movefile(src,dst,'f'); % rename the file
% movefile(fullfile(thisFolder,origName),fullfile(thisFolder,NewNames),'f')
if status == 0 % success
row = row + 1;
% fprintf('Renamed %s to %s \t\tin %s\n',origName,NewNames,thisFolder);
else
row2 = row2 + 1;
disp(cmdout)
warning('Failed to rename %s to %s in %s\n',origName,NewNames,thisFolder);
thisfailure = table({thisFolder},{origName},{NewNames},{illegals},...
list(i).bytes,{datestr(list(i).datenum)},{cmdout});
thisfailure.Properties.VariableNames = failures.Properties.VariableNames;
failures(row2,:) = thisfailure;
continue
end
else
row = row + 1;
end
else
if dorename
error('correctFileNamesOnWindows:OS:notmac',...
'This function is for Mac OS X only if dorename option is true');
else
row = row + 1;
end
end
thisrep = table({thisFolder},{origName},{NewNames},{illegals},...
list(i).bytes,{datestr(list(i).datenum)});
thisrep.Properties.VariableNames = report.Properties.VariableNames;
report(row,:) = thisrep;
end
end
end
end
%--------------------------------------------------------------------------
function [illegals,illegal_count,NewNames] = findillegals(target,replacement,...
NewNames,illegals,illegal_count)
targetreg = regexptranslate('escape',target);
if isregexpmatched(NewNames,targetreg)
startIndex = regexp(NewNames,targetreg);
NewNames = regexprep(NewNames,targetreg,replacement);
for k = 1:length(startIndex)
illegals(illegal_count + k).char = target;
illegals(illegal_count + k).newchar = replacement;
illegals(illegal_count + k).startIndex = startIndex(k);
end
illegal_count = illegal_count + length(startIndex);
end
end
%--------------------------------------------------------------------------
function pathout = findQinpath(path)
%
% pathout = findQinpath(path)
%
% findQinpath searches for ' or "
%
% If only 's or "s are found, then encompass path with the other quotation
% mark.
%
% If both ' and " are found, make sure they are escaped by \?
%
%
%NOTE
% unix() takes bash command. By default, you don't need to use
% quatations for file names, but that makes you escape all the
% space and other special characters in file path. And the code
% gets difficult to read.
%
% You can use double " or single ' quotation marks for string. So
%
% unixt('mv "filepath1" "filepath2")
%
% may be generally a good choice, because you don't need to escpae special
% characters within quatation marks and " does not interfere with MATLAB's
% use of '.
%
% When a quotation mark is used in a file path, then you need to be
% careful. If only single quotation mark (') is used, then you can use
% doble quotation marks for the entire file path string, and vice versa.
%
% In case the file path uses both single and double quotation
% marks, then you can only escape with \.
singlefound = isregexpmatched(path,'''');
doublefound = isregexpmatched(path,'"');
if singlefound && ~doublefound
pathout = ['"',path,'"'];
elseif ~singlefound && doublefound
pathout = ['''',path,''''];
elseif ~singlefound && ~doublefound
pathout = ['"',path,'"'];
elseif singlefound && doublefound
path1 = regexptranslate('escape',path);
path2 = regexprep(path1,'\s','\\ ');
path3 = regexprep(path2,'"','\\"'); % need to escape "
pathout = regexprep(path3,'''','\\'''); % need to escape '
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment