我对bash脚本没什么了解。我编写了这个脚本来更新我目录中的所有存储库。但是不是像它应该那样经历所有的回购,而是仅通过第一个存储库并在第一个存储库上循环。引起它的代码中的错误是什么?
代码:
rm -rf log.txt
touch log.txt
ls > log.txt
for (( i = 0; i < $(cat log | wc -l); i++ )); do
dir_name=$(sed "${i+1}q;d" log.txt)
cd ${dir_name}
git pull origin master
cd ..
done
答案 0 :(得分:2)
您的脚本非常复杂,您可以尝试:
for dir_name in "$(find -maxdepth 1 -type d)"; do
cd "$dir_name" && git pull origin master && cd - || break
done
编辑(正确处理空格 ):
find -maxdepth 1 -type d -print0 | while read -d $'\0' dir_name; do
( cd "$dir_name" && git pull origin master ) || break
done
如果并非所有目录都是git存储库,您可以使用它:
for dir_name in "$(find -maxdepth 1 -type d)"; do
find -maxdepth 1 -type d -print0 | while read -d $'\0' dir_name; do
(
cd "$dir_name" || exit $?
[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ] ||
{ echo "$dir_name: not a git repository"; exit 0; }
git pull origin master || exit $?
) || break
done
答案 1 :(得分:2)
rm -rf log.txt touch log.txt ls > log.txt
在bash中创建临时文件的理由很少。像这样的简单脚本应该能够通过管道处理所有内容 - 甚至更好,只需要生成文件名。
for (( i = 0; i < $(cat log | wc -l); i++ )); do
这是一种在bash中进行循环的真正,非常,罕见和模糊的方法。正常循环是
for variable in words…; do …; done
在这种情况下,您根本不需要ls
,因为shell可以通过文件名生成自己生成文件列表。所以只是:
for dir_name in *; do
如果使用变量或shell扩展,它将被拆分,但是在空格上。您可以使用while循环进行其他拆分(参见下文)。
dir_name=$(sed "${i+1}q;d" log.txt)
我们通过直接遍历名称来替换此行。但是我应该注意,这个行是原始代码不起作用的原因(除了缺少的扩展名)。问题是
${variable+value}
是不是算术表达式。相反,如果value
已定义,则返回variable
,否则返回任何内容。因此${i+1}
总是 1
。
算术表达式用$(())
编写。
cd ${dir_name}
通常只有在需要从以下文本中分隔变量名时才使用{}
。但是, 所做的是引用扩展,因为目录名可能包含空格,然后shell将拆分名称,命令将失败 - 或者更糟糕的是,危险的东西!所以
cd "$dir_name"
git pull origin master cd ..
如果cd "$dir_name"
由于某种原因失败,cd ..
将带您到您不想要的地方:
$dir_name
是一个文件,则第一个cd
将不执行任何操作,第二个将向您提升一个级别$dir_name
是符号链接,则第一个cd
将解析它,第二个将带您到实际目录的父级。我通常会在这里使用子shell:
(
cd "$dir_name"
git pull origin master
)
()
创建一个子shell。对变量和当前目录所做的所有更改都是该块的本地更改。
或者,您可以使用pushd
和popd
:
pushd "$dir_name"
git pull origin master
popd
但这需要一个bash,而我在这里写的其余部分是POSIX shell。编写没有bash扩展的shell很好,因为许多系统在/bin/sh
下都有一个POSIX shell,比bash
快,但没有大部分扩展。
done
我提到了一个while循环。如果您确实希望通过每行输出一个的程序生成值,而不是for
循环,那么您将进入while
循环。因此,如果您想在此使用ls
(没有充分理由),请将for
行替换为:
ls | while read -r dir_name; do
read
命令从输入读取一行并将指定的变量设置为它(当没有要读取的行时失败)。然后while
运行,直到命令失败。 -r
标志告诉它不要解释\
转义序列。我不记得ls
的转义模式是否与read
预期的匹配 - 如果是,那将是更可靠的方式。
答案 2 :(得分:0)
cat&#39; log.txt&#39;而不是&#39; log&#39;
rm -rf log.txt
touch log.txt
ls > log.txt
for (( i = 1; i <= $(cat log.txt | wc -l); i++ )); do
dir_name=$(sed "${i}q;d" log.txt)
cd ${dir_name}
git pull origin master
cd ..
done
答案 3 :(得分:-1)
这是另一种可行的方法。
ls -F | grep -e "/$" | while read repo
do
git --git-dir=${repo}.git pull origin master
done