如何编写 Bash 单行命令来克隆和管理 GitHub 和 GitLab 存储库 一个 Bash 单行命令来克隆所有 GitHub 存储库 一个 Bash 单行命令来在 GitLab 上创建和推送多个存储库 详细说明 Bash 单行命令的简洁性

2025-06-07

如何编写 Bash 单行命令来克隆和管理 GitHub 和 GitLab 存储库

用 Bash 单行命令克隆所有 GitHub 存储库

使用 Bash 单行命令在 GitLab 上创建和推送多个存储库

阐述 Bash 单行命令的简洁性

编辑:这篇文章的早期版本标题似乎暗示我鼓励大家离开 GitHub。事实上,这并不是我的本意,所以标题已修改。

对我来说,没有什么比一行优雅的 Bash 代码自动完成数小时繁琐的工作更令人满足的了。最近,我尝试用 Bash 脚本自动重建我的笔记本电脑(之后会发布!),希望找到一种方法,轻松地将我托管在 GitHub 上的仓库克隆到新机器上。经过一番摸索,我编写了一个单行代码来实现这一点。然后,本着不把鸡蛋放在同一个篮子里的精神,我又编写了一个单行代码,用于自动创建并推送到 GitLab 托管的备份。代码如下。

用 Bash 单行命令克隆所有 GitHub 存储库

注意:你需要一个要克隆的 GitHub 仓库列表。这样做的好处是,你可以完全自主地选择在你的机器上克隆哪些仓库,而不用费尽心思地克隆所有仓库。

你可以使用 HTTPS 和15 分钟缓存凭证轻松克隆 GitHub 仓库,而无需每次都输入密码,或者,我推荐使用 SSH 连接到 GitHub。为了简洁起见,我假设我们采用后者,并且我们的 SSH 密钥已经设置好了。

给出文件中的 GitHub URL 列表gh-repos.txt,如下所示:

git@github.com:username/first-repository.git
git@github.com:username/second-repository.git
git@github.com:username/third-repository.git
Enter fullscreen mode Exit fullscreen mode

我们运行:

xargs -n1 git clone < gh-repos.txt
Enter fullscreen mode Exit fullscreen mode

这会将列表中的所有仓库克隆到当前文件夹中。同样的方法也适用于 GitLab 仓库,只需替换相应的 URL 即可。

这里发生了什么?

这行代码分为两部分:右侧是输入部分(这有点违反直觉),左侧是执行部分。我们可以像这样编写相同的命令,让这两部分的顺序更直观一些(或许可以?):

<gh-repos.txt xargs -n1 git clone 
Enter fullscreen mode Exit fullscreen mode

要对输入的每一行运行一个命令gh-repos.txt,我们使用xargs -n1。该工具xargs会从输入中读取项目并执行它找到的任何命令(echo如果找不到,则会执行)。默认情况下,它假定项目之间用空格分隔;换行符也适用,这使得我们的列表更易于阅读。 标志-n1指示xargs使用1参数,或者在本例中,每个命令一行。我们使用 构建命令git clonexargs然后对每一行执行。Ta-da。

使用 Bash 单行命令在 GitLab 上创建和推送多个存储库

与 GitHub 不同,GitLab 允许我们做一件非常巧妙的事情,无需先通过网站创建新的仓库。我们可以从终端创建一个新的 GitLab 仓库。新创建的仓库默认设置为私有,因此如果我们想在 GitLab 上将其设置为公开,则必须稍后手动进行操作。

GitLab 文档建议我们使用 来推送创建新项目git push --set-upstream,但我发现这对于使用 GitLab 作为备份来说不太方便。以后我处理我的代码库时,希望运行一个命令就能同时推送到 GitHubGitLab,而无需我做额外的工作。

为了使这行 Bash 代码正常工作,我们还需要 GitLab 的仓库 URL 列表(其中可能还不存在)。我们可以轻松地做到这一点,只需复制我们的 GitHub 仓库列表,用 Vim 打开它,然后执行搜索并替换即可:

cp gh-repos.txt gl-repos.txt
vim gl-repos.txt
:%s/\<github\>/gitlab/g
:wq
Enter fullscreen mode Exit fullscreen mode

这将产生gl-repos.txt,如下所示:

git@gitlab.com:username/first-repository.git
git@gitlab.com:username/second-repository.git
git@gitlab.com:username/third-repository.git
Enter fullscreen mode Exit fullscreen mode

我们可以在 GitLab 上创建这些存储库,将 URL 添加为远程,然后通过运行以下命令将我们的代码推送到新的存储库:

awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt
Enter fullscreen mode Exit fullscreen mode

稍等一下,我会解释的;现在,请注意,这~/FULL/PATH/应该是包含我们的 GitHub 存储库的目录的完整路径。

我们确实必须注意几个假设:

  1. 本地计算机上包含存储库的目录名称与 URL 中的存储库名称相同(如果使用上面的单行命令克隆,则情况就是这样);
  2. 每个存储库当前都已检出到您想要推送的分支,即master。。

可以扩展单行代码来处理这些假设,但作者认为,在那时,我们真的应该编写一个 Bash 脚本。

这里发生了什么?

我们的 Bash 单行命令将gl-repos.txt文件中的每一行(或 URL)作为输入。使用awk,它会拆分出本地计算机上包含存储库的目录名称,并使用这些信息来构建更大的命令。如果我们查看print的输出awk,我们会看到:

cd ~/FULL/PATH/first-repository && git remote set-url origin --add git@gitlab.com:username/first-repository.git && git push
cd ~/FULL/PATH/second-repository && git remote set-url origin --add git@gitlab.com:username/second-repository.git && git push
cd ~/FULL/PATH/third-repository && git remote set-url origin --add git@gitlab.com:username/third-repository.git && git push
Enter fullscreen mode Exit fullscreen mode

让我们看看如何构建这个命令。

分割字符串awk

该工具awk可以根据字段分隔符来拆分输入。默认分隔符是空格字符,但我们可以通过传递-F标志来更改它。除了单个字符外,我们还可以使用正则表达式字段分隔符。由于我们的存储库 URL 具有固定格式,因此我们可以通过查询斜杠字符/和 URL 末尾之间的子字符串 来获取存储库名称.git

实现这一点的一种方法是使用我们的正则表达式\/|(\.git)

  • \/是转义/字符;
  • |表示“或”,告诉 awk 匹配任一表达式;
  • (\.git)是 URL 末尾与“.git”匹配的捕获组,带有转义.字符。这有点像作弊,因为“.git”严格来说并没有分割任何东西(另一边什么也没有),但这对我们来说是一种简单的去掉这一部分的方法。

一旦我们确定了awk拆分位置,就可以使用字段运算符来获取正确的子字符串。我们先用字符 来引用字段$,然后再使用字段的列号。在我们的示例中,我们需要第二个字段 。$2所有子字符串如下所示:

1: git@gitlab.com:username
2: first-repository
Enter fullscreen mode Exit fullscreen mode

要使用整个字符串(或者在本例中是整个 URL),我们使用字段运算符$0。要编写命令,我们只需将字段运算符替换为存储库名称和 URL。在print构建过程中运行它可以帮助确保所有空格都正确无误。

awk -F'\/|(\.git)' '{print "cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push"}' gl-repos.txt
Enter fullscreen mode Exit fullscreen mode

运行命令

我们在 的括号内构建命令system()。通过将其用作 的输出awk,每个命令将在构建和输出后立即运行。该system()函数创建一个子进程来执行我们的命令,并在命令完成后返回。简而言之,这使我们能够在每个存储库上逐个执行 Git 命令,而无需中断awk正在处理输入文件的主进程。以下是我们最终的命令,所有内容都已整合在一起。

awk -F'\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt
Enter fullscreen mode Exit fullscreen mode

使用我们的备份

通过将 GitLab URL 添加为远程仓库,我们简化了向两个外部托管仓库推送代码的流程。如果我们git remote -v在其中一个仓库目录中运行,将会看到:

origin git@github.com:username/first-repository.git (fetch)
origin git@github.com:username/first-repository.git (push)
origin git@gitlab.com:username/first-repository.git (push)
Enter fullscreen mode Exit fullscreen mode

现在,只需运行git push而不使用参数即可将当前分支推送到两个远程存储库。

我们还应该注意,git pull通常只会尝试从您最初克隆的远程仓库(上面示例中标记的 URL (fetch))拉取。同时从多个 Git 仓库拉取是可以的,但比较复杂,超出了本文的讨论范围。如果您感兴趣的话,这里有一个关于向多个远程仓库推送和拉取的说明,可以帮助您入门。Git远程仓库文档也可能对您有所帮助。

阐述 Bash 单行命令的简洁性

如果理解了 Bash 的单行命令,它们可以成为有趣且方便的快捷方式。至少,了解像xargs和这样的工具awk可以帮助我们实现自动化,并减少工作中的许多繁琐步骤。然而,它也有一些缺点。

就易理解、易维护和易上手性而言,Bash 单行程序很糟糕。它们的编写通常比使用循环ifwhile循环的 Bash 脚本更复杂,阅读起来也更困难。我们编写它们时很可能会漏掉某个地方的单引号或右括号;而且,正如我希望这篇文章能够说明的那样,它们也需要相当多的解释。那么,为什么要使用它们呢?

想象一下,你一步一步地阅读着一份烘焙蛋糕的食谱。你了解了制作方法和配料,并准备好了所有材料。然后,你开始思考,只要把所有材料按照正确的顺序放进烤箱,蛋糕就能瞬间出炉。你试了一下,果然成功了!

那将会非常令人满意,不是吗?

文章来源:https://dev.to/victoria/bash-one-liners-to-clone-all-your-github-repositories-and-back-them-up-to-gitlab-28n4
PREV
黑客正在谷歌搜索你的纯文本密码:防止敏感数据泄露
NEXT
关于时间