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

找不到文件时,ls上的同步shell脚本失败,shell,zsh,Shell,Zsh,我已经编写了一个shell脚本,将我的dotfiles存储库同步到我的主目录。这在Cygwin(zsh)中运行良好,但我刚刚迁移到Linux(Xubuntu 12.10上的zsh),它失败了 脚本将在repo中列出相关的点文件,然后在创建符号链接之前,它将归档任何冲突。归档程序如下所示: for i in $dotFiles; do toArchive=$toArchive $(ls -d $i) done #!/bin/zsh # Break on first error and un

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

脚本将在repo中列出相关的点文件,然后在创建符号链接之前,它将归档任何冲突。归档程序如下所示:

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i)
done
#!/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
中的任何项目不存在时,此操作失败
ls
返回
没有这样的文件或目录
,脚本终止

将stderr重定向到
/dev/null
会解决这个问题吗?i、 e:

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i 2>/dev/null)
done
……还是有更好的解决方案


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

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

 cmd || :
(读:command或true)。做

玩这个把戏


我看了你的剧本。事实上,
set-e
是有效的。您可能希望完全删除它,或者只在脚本的特定区域有选择地打开它(
set+e
撤消
set-e
)的效果。

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

 cmd || :
(读:command或true)。做

玩这个把戏


我看了你的剧本。事实上,
set-e
是有效的。您可能希望完全删除它,或者仅在脚本的特定区域有选择地打开它(
set+e
撤消
set-e
)的效果。

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

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

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

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

如果您想知道的话,它还将zsh的简短条件语法用于
if

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

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

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

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

如果您想知道的话,它还将zsh的短条件语法用于
if

shell脚本最糟糕的事情就是让它工作。第二个最糟糕的情况是维护(在六个月内无法计算出每条线路/选项的功能)。考虑到这一点,我建议您只需添加一个存在检查:

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

shell脚本最糟糕的事情就是让它工作。第二个最糟糕的情况是维护(在六个月内无法计算出每条线路/选项的功能)。考虑到这一点,我建议您只需添加一个存在检查:

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

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

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i)
done
#!/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
当我在做的时候,我让它稍微更健壮一些:

  • 有两类点文件,公共和私有。私有目录用于
    .gnupg
    等内容。它的内容被Git忽略,因此它们永远不会被推出
  • 如果脚本从其自己的目录运行,则项目文件夹可以位于任何位置,然后脚本将计算出绝对路径

我已将此处发布的建议逐一采纳,并将其打包成一个完整的解决方案。我不知道是什么打破了我脚本中的逻辑,但如果我猜的话,我会说它可能是在解析
ls
的结果和/或使用一个空格字符串,而不是zsh数组。。。无论如何,工作脚本如下所示:

for i in $dotFiles; do
  toArchive=$toArchive $(ls -d $i)
done
#!/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
当我在做的时候,我让它稍微更健壮一些:

  • 有两类点文件,公共和私有。私有目录用于
    .gnupg
    等内容。它的内容被Git忽略,因此它们永远不会被推出
  • 如果脚本从其自己的目录运行,则项目文件夹可以位于任何位置,然后脚本将计算出绝对路径

谢谢:)我回家后会试试这个,让你知道!。。。当我将脚本发布到时,向我推荐了
set
标志。我保留它们,因为它似乎是明智的,特别是考虑到我[缺乏]shell脚本编写经验!。。。我做了你建议的更改,但仍然不起作用:脚本现在只是迭代所有丢失的点文件,抱怨它们不存在,