Reputation: 9211
I have written a shell script to sync my dotfiles repository to my home directory. This was working fine in Cygwin (zsh), but I've just migrated to Linux (zsh on Xubuntu 12.10) and it's failing.
The script makes a list of the relevant dotfiles in the repo then, before creating symlinks, it archives any clashes. The archiving routine is as follows:
for i in $dotFiles; do
toArchive=$toArchive $(ls -d $i)
done
This fails when any item in $dotfiles
doesn't exist; ls
returns No such file or directory
and the script terminates.
Would redirecting stderr
to /dev/null
resolve this? i.e.:
for i in $dotFiles; do
toArchive=$toArchive $(ls -d $i 2>/dev/null)
done
...or is there a better solution?
(My script is here, for sake of completeness.)
Upvotes: 1
Views: 1207
Reputation: 9211
I have taken the suggestions posted here individually and wrapped them into a whole solution. I don't know what it was that was breaking the logic in my script, but if I were to guess, I'd say it was probably parsing the results of ls
and/or using a spaced string, rather than a zsh array... Anyway, the working script is as follows:
#!/bin/zsh
# Break on first error and unset variables
set -e
set -u
# Gather dotfiles
repoDir=$(pwd)
cd $repoDir/public
publicDotfiles=(.*)
cd $repoDir/private
privateDotfiles=(.*)
# Determine clashes, to backup
typeset -a toBackup
for file in $publicDotfiles $privateDotfiles; do
dotFile=$HOME/$file
if [[ -e $dotFile ]] toBackup=($toBackup $dotFile)
done
if [[ "$toBackup" != "" ]]; then
backupFile=$HOME/dotfiles.$(date +%Y%m%d).tar.gz
tar czPf $backupFile $toBackup --remove-files
fi
# Create symlinks
for i in $publicDotfiles; do
ln -s $repoDir/public/$i $HOME/$i
done
for i in $privateDotfiles; do
ln -s $repoDir/private/$i $HOME/$i
done
While I was at it, I made it slightly more robust:
.gnupg
, etc. Its contents are ignored by Git, so they never get pushed out.Upvotes: 0
Reputation:
Zsh, unlike other shells, does not execute a command when the file name pattern does not match anything. Meaning an echo hello *
in an empty directory will not actually print hello *
on the screen. Instead, zsh will print said error message and echo
will not get executed.
You can switch that on and off with one of those funky options called NOMATCH
which is active by default. Read zsh's info pages on how it works, please. And chapter 2 in the zsh FAQ also explains about this.
However, if I interpret your intention correctly then you have a list of file names that may or may not actually exist, and you only want to include a file name in the backup if it exists. If that is the case then here is a better version using arrays and file existence tests:
typeset -a files_to_backup
for file in $dotFiles ; do
if [[ -f $file ]] files_to_backup=($files_to_backup $file)
done
This also uses zsh's short conditional syntax for the if
, in case you're wondering.
Upvotes: 1
Reputation: 4110
The worst thing with a shell-script is making it work. The second worst is maintaining (having t figure out in six months time what each line/option did). With that in mind, I suggest you to just add an existence check:
for i in $dotFiles; do
if [[ -e $i ]]; then
toArchive=$toArchive $(ls -d $i)
fi
done
Upvotes: 2
Reputation: 72667
I don't understand exactly why your script terminated on the first error (set -e
in effect? Running as part of a Makefile rule?), but ignoring the non-zero status of any command can be achieved by running
cmd || :
(read: command or true). Does
$(ls -d $i || :)
do the trick?
PS: I looked at your script. In fact, set -e
is in effect. You might want to remove it completely or selectively turn it on only for specific regions of your script (set +e
undoes the effect of set -e
).
Upvotes: 1