没有什么比让Bash自动化完成数小时繁琐工作的Bash优雅系列更令我满意的了。 作为最近使用Bash脚本自动重新创建笔记本电脑的探索的一部分,我想找到一种方法,可以轻松地将GitHub托管的存储库克隆到新计算机上。 经过一番挖掘之后,我写了一篇这样的单线纸。 然后,本着不把所有鸡蛋都放在同一篮子的精神,我写了另一种单行代码来自动创建并推送到GitLab托管的备份。 他们来了。
一个Bash单一代码来克隆您所有的GitHub存储库
警告:您将需要要克隆的GitHub存储库的列表。 这样做的好处是,它为您提供了全面的代理权,使您可以只选择要在计算机上存储的存储库,而不必花很多精力。
您可以轻松地克隆GitHub存储库,而无需每次都输入HTTPS并使用15分钟的缓存凭据,或者,我首选的方法是使用SSH连接到GitHub,而无需每次输入密码。 为简便起见,我假设我们要使用后者,并且我们已经设置了SSH密钥。
在gh-repos.txt
文件中给出GitHub URL列表,如下所示:
git@github.com:username/first-repository.git
git@github.com:username/second-repository.git
git@github.com:username/third-repository.git
我们跑:
xargs -n1 git clone < gh-repos.txt
这会将列表上的所有存储库克隆到当前文件夹中。 如果替换适当的URL,则同一行也适用于GitLab存储库。
这里发生了什么?
单行代码有两半:违反直觉的输入在右侧,而使事情发生的部分在左侧。 通过编写如下相同的命令,我们可以使这些部分的顺序更直观(也许?):
<gh-repos.txt xargs -n1 git clone
要对输入的每一行gh-repos.txt
运行命令,我们使用xargs -n1
。 xargs
工具从输入中读取项目并执行找到的所有命令(如果找不到则echo
)。 默认情况下,它假定项目之间用空格隔开; 新行也可以使我们的列表更易于阅读。 标志-n1
告诉xargs
每个命令使用1
参数,在本例中为1行。 我们使用git clone
构建命令,然后xargs
将针对每一行执行。 -
Bash一线式工具,可在GitLab上创建并推送许多存储库
与GitHub不同,GitLab让我们可以做这件漂亮的事情,而不必先使用网站创建新的存储库。 我们可以从终端创建一个新的GitLab存储库 。 新创建的存储库默认设置为“私有”,因此,如果要在GitLab上将其设置为“公开”,则稍后必须手动进行。
GitLab文档告诉我们使用git push --set-upstream
来推动创建一个新项目,但是我觉得使用GitLab作为备份不是很方便。 将来在使用存储库时,我想运行一个命令同时推送到GitHub 和 GitLab,而无需我付出额外的努力。
为了使此Bash单线工作,我们还需要GitLab的存储库URL列表(尚不存在的URL)。 我们可以轻松地做到这一点,方法是复制我们的GitHub存储库列表,使用Vim打开它,然后进行搜索和替换 :
cp gh-repos.txt gl-repos.txt
vim gl-repos.txt
:%s/\<github\>/gitlab/g
:wq
这将产生gl-repos.txt
,看起来像:
git@gitlab.com:username/first-repository.git
git@gitlab.com:username/second-repository.git
git@gitlab.com:username/third-repository.git
我们可以在GitLab上创建这些存储库,将URL添加为远程存储,并通过运行以下命令将代码推送到新的存储库:
awk -F '\/|(\.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt
请稍等,我会解释。 现在,请注意~/FULL/PATH/
应该是包含我们的GitHub存储库的目录的完整路径。
我们必须注意以下两个假设:
- 包含资源库的本地计算机上目录的名称与URL中资源库的名称相同(如果是使用上述一种方法克隆的,则为这种情况);
- 当前,每个存储库都检出到要推送的分支,即。
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
让我们看看如何构建此命令。
用awk
分割字符串
工具awk
可以基于字段分隔符拆分输入。 默认的分隔符是空格字符,但是我们可以通过传递-F
标志来更改它。 除了单个字符,我们还可以使用正则表达式字段分隔符 。 由于我们的存储库URL具有固定的格式,因此我们可以通过请求斜杠/
和URL末尾.git
之间的子字符串来获取存储库名称。
实现此目的的一种方法是使用我们的正则表达式\/|(\.git)
:
-
\/
是转义的/字符; -
|
表示“或”,告诉awk匹配任一表达式; -
(\.git)
是URL末尾的捕获组,与“ .git”匹配,带有转义符.
字符。 这有点作弊,因为“ .git”并没有严格分割任何内容(另一面没有任何内容),但这是我们轻松实现这一点的简便方法。
告诉awk
在哪里分割后,我们可以使用field运算符来获取正确的子字符串。 我们用$
字符引用字段,然后用字段的列号引用。 在我们的示例中,我们需要第二个字段$2
。 这是所有子字符串的样子:
1: git@gitlab.com:username
2: first-repository
要使用整个字符串或本示例中的整个URL,我们使用字段运算符$0
。 要编写该命令,我们只需将字段运算符替换为存储库名称和URL。 在我们构建时使用print
运行它可以帮助确保所有空间都正确。
awk -F '\/|(\.git)' '{print "cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push"}' gl-repos.txt
运行命令
我们在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
使用我们的备份
通过将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)
现在,简单地运行不带参数的git push
会将当前分支推送到两个远程存储库。
我们还应该注意, git pull
通常只会尝试从最初克隆的远程存储库中(fetch)
在上面的示例中标记为(fetch)
的URL)。 可以同时从多个Git存储库中拉出,但很复杂,超出了本文的范围。 如果您有好奇心,这是推和拉到多个遥控器以帮助您入门的说明。 遥控器上的Git文档也可能会有所帮助。
详细阐述Bash单行代码的简洁性
当了解Bash一线时,它可能是有趣且方便的快捷方式。 至少,了解xargs
和awk
类的工具可以帮助自动化和减轻工作中的繁琐工作。 但是,还有一些缺点。
就易于理解,易于维护和易于使用的工具而言,Bash一线难熬。 与使用if
或while
循环的Bash脚本相比,编写它们通常更复杂,并且读取起来当然更复杂。 当我们编写它们时,可能会在某处遗漏单引号或右括号。 正如我希望这篇文章所演示的那样,他们也可以做很多解释。 那为什么要使用它们呢?
想象一下,逐步阅读烘焙蛋糕的食谱。 您了解方法和成分,并收集物品。 然后,当您考虑它时,您开始意识到,如果只是以正确的顺序将所有配料扔进烤箱,蛋糕就会立即变质。 您尝试一下,就可以了!
那会很令人满意,不是吗?