使用空格的Bash脚本,git和文件名

时间:2012-09-09 16:41:02

标签: git bash

我正在尝试使用bash脚本自动化我与git的一些交互。为此,我有一个脚本,将ssh并列出所有存储库,然后克隆它们。部分脚本在此处显示

DIRS=`ssh $u "ls $p"`;
for DIR in $DIRS; do
    echo "ssh://$u$p$DIR"
    echo "git clone ssh://$u$p$DIR";  
    git clone ssh://$u$p$DIR
done

示例参数是

-u user@host

-p /cygdrive/c/Documents\ and\ Settings/somePath

这对于名称中包含空格的文件路径很好,直到git clone部分抛出太多空格的错误。如果我复制并粘贴了

回显的部分
echo "git clone ssh://$u$p$DIR";
然后它克隆得很好。我试图用引号括起来,但它会引发错误

does not appear to be a git repository

我做错了什么?

修改1

使用git clone "ssh://$u$p$DIR"时的错误是

fatal: '/cygdrive/c/Documents\ and\ Settings/somePath/repo1' does not appear to be a git repository
fatal: The remote end hung up unexpectedly

5 个答案:

答案 0 :(得分:3)

如果您的目录名称包含空格,那么您肯定不希望这样做:

DIRS=`ssh $u "ls $p"`;
for DIR in $DIRS; do

由于您希望保留空格而不是将它们作为分隔符使用,因此通常接受的方法是告诉bash分隔符是换行符:

IFS=$'\n'
for DIR in `ssh $u "ls -1 $p"`; do

如果你真的必须拥有变量DIRS,你可以使用bash的数组变量(这将保留你的空格):

IFS=$'\n'
DIRS=(`ssh $u "ls -1 $p"`)
unset IFS
for DIR in "${DIRS[@]}"; do

另一种常规形式是使用read

ssh $u "ls -1 $p" | while read DIR; do

或者:

while read DIR; do
    ...
done < <(ssh $u "ls -1 $p")

但是,建议不要使用这些错误处理(尤其是在ssh开始使用管道时会更加复杂。)

到目前为止,所有内容都只是基本的bash脚本。

至于这一行,你想要用双引号括起第二个参数,因为$DIR中的一个空格会产生第三个参数:

git clone "ssh://$u$p$DIR"

我在git中试过这个,但它似乎有效。然而,正如@holygeek所说,用%20替换空格似乎也有效,并且在某些情况下可能有益。你可以这样做:

git clone "ssh://$u$p${DIR// /%20}"

答案 1 :(得分:1)

您的问题几乎肯定是文件名中的空格

findxargs可以支持一种特殊模式,即使用\0(null)字符而不是空格分隔文件名。

试试这个:

ssh $u find $p -depth 0 -print0 | xargs -0 -J % git clone ssh://$u%

您可以通过运行

轻松调试示例中的脚本
bash -x myscript.sh

然后它将在执行之前打印出每个命令行。这应该让你在问题出现之前找到实际运行的内容。

答案 2 :(得分:1)

你有两个问题。一个是你使用了ls,它可以为文件名做各种奇怪的事情。 Don't parse the output of ls.另一个是你没有在$u$p$DIR附近加双引号。 总是在命令替换周围添加双引号(除非变量的值是以空格分隔的glob模式列表,它很少是这样)。

请注意,一般情况下,$p也需要引用。由于它是通过ssh,你不能避免它被解释在另一边。如果$p不包含任何shell特殊字符(如空格),则这并不太难。如果确实如此,保护它们的最简单方法是在本地扩展$p,并在其周围加上单引号。单引号形成远程shell的字符串文字,除非$p包含单引号。这些需要由序列'\''替换。 Bash对${p//PATTERN/REPLACEMENT}结构的解析规则有点奇怪;使用中间变量$q的两步流程可以完成工作。

如果您可以依赖不包含任何换行符的文件名,则可以在远程端每行打印一个文件名。

q=\'\\\'\'
ssh "$u" "cd '${p//\'/$q}' && for x in *; do echo \"\$x\"; done" | {
  p=${p%/}
  p=${p//%/%3f}
  case $p in
    /*) :;;
    *) p="/~/$p";;
  esac
  while IFS= read -r DIR; do
    DIR=${DIR//%/%3f}
    git clone "ssh://$u$p/$DIR"
  done
}

请注意,$p$DIR中的某些特殊字符需要在git ssh:网址中进一步引用。空格没问题,但%至少需要转义为%3f。在上面的代码中,我添加了对此的支持,以及对$p是相对路径(将相对于主目录进行解释)的情况的支持。

答案 3 :(得分:0)

我使用Gilles'sAntak's回答中的信息来查找解决方案以及running ssh on a remote machineusing variables in a bash heredoc上的其他帖子。

根据Gilles的建议,我避免使用ls,并且我使用了Antak的建议,即使用IFS=$'\n' 来避免空格中的多个参数,并在换行符上拆分参数。

我已经确定的最终代码是

DIRS=`ssh "$u" << ENDSSH
    cd "$p"
    eval 'for file in *; do echo \\$file; done'
ENDSSH`

IFS=$'\n'
for DIR in $DIRS;
    do
    echo "ssh://$u$p/$DIR"
done
unset IFS 

答案 4 :(得分:-2)

查看用%20替换空格是否适合您。