我正在尝试使用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
答案 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)
您的问题几乎肯定是文件名中的空格。
find
和xargs
可以支持一种特殊模式,即使用\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's和Antak's回答中的信息来查找解决方案以及running ssh on a remote machine和using 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替换空格是否适合您。