当找不到文件时,同步shell脚本在ls上失败

时间:2012-12-10 10:28:57

标签: shell zsh

我编写了一个shell脚本来将我的dotfiles存储库同步到我的主目录。这在Cygwin(zsh)中运行良好,但我刚刚迁移到Linux(zsh on Xubuntu 12.10)并且它失败了。

该脚本在repo中列出了相关的dotfiles,然后在创建符号链接之前,它会存档任何冲突。归档程序如下:

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i)
done

$dotfiles中的任何项目不存在时,此操作失败; ls返回No such file or directory,脚本终止。

stderr重定向到/dev/null会解决此问题吗?即:

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i 2>/dev/null)
done

...还是有更好的解决方案?

(我的脚本是here,为了完整起见。)

4 个答案:

答案 0 :(得分:1)

我不明白为什么你的脚本在第一个错误(set -e生效??作为Makefile规则的一部分运行?)中终止的原因,但忽略任何命令的非零状态都可以通过运行

 cmd || :

(读取:命令或true )。确实

  $(ls -d $i || :)

诀窍?

PS:我看了你的剧本。实际上,set -e已生效。您可能希望将其完全删除,或者仅针对脚本的特定区域选择启用它(set +e撤消set -e的效果。

答案 1 :(得分:1)

与其他shell不同,Zsh在文件名模式与任何内容不匹配时不执行命令。这意味着空目录中的echo hello *实际上不会在屏幕上打印hello *。相反,zsh将打印出错误消息并且echo将不会被执行。

您可以使用默认情况下处于活动状态NOMATCH的一个时髦选项来打开和关闭它。请阅读zsh关于其工作原理的信息页面。 chapter 2 in the zsh FAQ也解释了这一点。

但是,如果我正确地解释了您的意图,那么您有一个可能存在或可能不存在的文件名列表,并且您只希望在备份中包含文件名(如果存在)。如果是这种情况,那么这里是使用数组和文件存在测试的更好的版本:

typeset -a files_to_backup
for file in $dotFiles ; do
  if [[ -f $file ]] files_to_backup=($files_to_backup $file)
done

如果您想知道,这也会为if使用zsh的短条件语法。

答案 2 :(得分:1)

shell脚本最糟糕的事情就是让它工作。第二个最糟糕的是维持(在六个月内计算出每条线/期权的作用)。考虑到这一点,我建议你只添加一个存在检查:

for i in $dotFiles; do
  if [[ -e $i ]]; then
      toArchive=$toArchive $(ls -d $i)
  fi
done

答案 3 :(得分:0)

我已经将这里发布的建议单独提取并整理成一个完整的解决方案。我不知道是什么打破了我脚本中的逻辑,但如果我猜测,我会说它可能正在解析ls的结果和/或使用间隔字符串,而不是一个zsh数组......无论如何,工作脚本如下:

#!/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

当我参与其中时,我的表现稍微强一些:

  • 有两类dotfiles,public和private。私人目录用于.gnupg等等.Git会忽略它的内容,因此它们永远不会被推出。
  • 提供脚本是从它自己的目录运行的,项目文件夹可以在任何地方,然后脚本可以运行绝对路径。