How to shell script a list of files with their mod

2019-01-29 08:38发布

问题:

Disclaimer: This question exists because of Unity3D and its library issue.

I've tried getting current git-cache-meta.sh and use it on windows. It doesn't work.

The first error I got was on this line:

$ find `git ls-files`
find: invalid predicate `-'

Changing it to the original find $(git ls-files) also doesn't work (i.e. both syntaxes are no good).

The invalid predicate is certainly due to a file named such as "boo - foo.bar". And so I tried to run find "$(git ls-files)". Then it turned into a very ugly error/bug.

I wish to know why command substitution (the weird $() doesn't work on mingw, but, more importantly:

How can we make git-cache-meta.sh work properly on mingw?

As you can see below, I actually manage to answer this without that keyword -> properly. So I still hope someone might come up with a fast and clean solution <- reason why I'm using this script to begin with.

回答1:

The original script was written in an elegant and complex style (probably more so than necessary). I rewrote it with most of the complexity removed. I don't know which feature is breaking on mingw, but this is more likely to work:

GIT_CACHE_META_FILE=.git_cache_meta
case $@ in
    --store)
        git ls-files|while read file; do
            find "$file" \( -printf 'chown %U %p\n' \) \
               \( -printf 'chgrp %G %p\n' \) \
               \( -printf 'chmod %#m %p\n' \) \
               \( -printf 'touch -c -d -m "%TY-%Tm-%Td %TH:%TM:%TS" "%p"\n' \)
        done | tee $GIT_CACHE_META_FILE
        ;;
    --apply)
        sh -e $GIT_CACHE_META_FILE
        ;;
    *)
        echo "Usage: $0 --store|--apply"; exit 1
        ;;
esac

The only thing that's likely to go wrong is if one of the executables on your system doesn't support all the options than are used here. That would be: find, chown, chgrp, touch, and chmod.



回答2:

I found a big and convoluted, thus hard to port, and slow solution. Too slow. Waaaay too slow. Takes over a minute to go through my git, which have about 9k files. But, none the less, here it is, thanks to Dave Taylor, google and many man pages. Basically, use for and Dave's hack instead of find:

git ls-files -d > .temp_gitcachemeta
for s in $(git ls-files | grep -vf .temp_gitcachemeta | sed 's/ /_+_/g');
    do t="$(echo $s | sed s'/_+_/ /g')";
    echo "touch -c -m -d \"$(date -r "$t" +'%F %T')\" \"$t\"";
done
rm .temp_gitcachemeta ;;

Using temp file here because it seems like mingw also doesn't support process substitution.



回答3:

MinGW likes to call native Windows tools which add a Carriage Return (CR, \r) to the end of each line. So you can replace $(commandname) with $(commandname | sed $'s/\r$//') or something similar.

Another option is to try something more Unix-y, such as Interix/SFU/SUA or Cygwin or MSYS or AT&T UWIN.

As for metadata in general, your problem will be that things like chown, chgrp, chmod and touch will behave differently on the Windows® platform, and some things will not even exist, especially on FAT filesystems, whereas NTFS has a completely different set of metadata (different format for time stamps, ACLs instead of Unix user/group/other permissions, etc.) so it’s likely to require some development work from someone who understands the Unix scripting world and the NT platform well.

Finally, I’d like to say that this script is a security hole since it just evaluates stored commands, so if anyone else has got the ability to write to the .git_cache_meta file, they will own your system.