Bash脚本无法正确循环

时间:2017-04-28 07:00:45

标签: git bash

我对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

4 个答案:

答案 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。对变量和当前目录所做的所有更改都是该块的本地更改。

或者,您可以使用pushdpopd

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