Linux 小記 — Ubuntu 自動化配置

前言

工欲善其事,必先利其器。通過屢次的重複配置 ubuntu 開發壞境,我終於決定花點時間總結一下,並將其寫成一個自動化配置腳本。服務器實例:ubuntu 16.04,技術棧:shell,python。python

1. 主機名

能夠經過 hostname newname 修改主機名,不過最好是寫入 /etc/hostname 文件,重啓生效。爲了讓同一內網段的主機能夠經過主機名訪問,應在 /etc/hosts 中添加私有ip的解析。git

2. 命令提示符

與命令提示符相關的環境變量是 PS1,初始值爲:PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$',各字符解釋以下:github

#\u:當前登錄用戶名
#\h:當前主機名(如 ubuntu)
#\H:當前主機的域名全稱(ubuntu.ubuntu.com)
#\w:當前目錄(絕對路徑)
#\W:當前目錄的 basename(只顯示最後一級路徑)
#\$:通常用戶爲$,root 用戶爲#
#\t:當前時間(24小時制,HH:MM:SS)
#\T:當前時間(12小時)
#\@:當前時間(Am/PM)
#\d:當前日期
#\v:Bash 版本
#\V:Bash 的發佈版本號
#\S:Shell 名稱

對於我來講我只須要 \u、\h、\W(\w 若是多進幾個目錄敲命令的體驗就不好了),爲了讓命令行一目瞭然,最好給命令提示符加個顏色 PS1='${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\W\[\e[0m\]\$ ',顏色代碼解釋以下:shell

前景色 背景色 效果
30m 40;
31m 41;
32m 42;
33m 43;
34m 44;
35m 45;
36m 46; 天藍
37m 47;
#\033[背景;字體顏色m或者\e[背景;字體顏色m

#0   從新設置屬性到缺省設置  
#1   設置粗體  
#2   設置一半亮度(模擬彩色顯示器的顏色)  
#4   設置下劃線(模擬彩色顯示器的顏色)  
#5   設置閃爍  
#7   設置反向圖象  
#22  設置通常密度  
#24  關閉下劃線  
#25  關閉閃爍  
#27  關閉反向圖象

3. GNU Readline Library

Readline 的解釋:從終端獲取用戶輸入的字符流,辯認其中一些特定的字符序列,而後執行這些序列對應的函數或者宏。通俗一點講就是綁定熱鍵,好比在 bash 中默認按下 ctrl+a 執行的是光標回到行首的命令。ubuntu

此處我須要優化的是:一、Tab 補全時忽略大小寫;二、經過 ↑↓ 查詢已輸入關鍵字的歷史記錄。vim

vim ~/.inputrc

"\e[A": history-search-backward
"\e[B": history-search-forward

# auto complete ignoring case
set show-all-if-ambiguous on
set completion-ignore-case on

source ~/.inputrc

4. 歷史記錄

我須要:一、忽略重複的歷史命令;二、保存更多的歷史記錄;三、忽略特定的歷史記錄;四、新建的終端同步 history。centos

export HISTCONTROL=ignoreboth # ignoreboth=ignoredups:ignorespace
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTIGNORE='pwd:ls'

# make sure all terminals save history
shopt -s histappend
export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"

5. Git 配置

想要流暢地使用 git,我認爲有幾點必須配置:bash

5.1 在命令提示符上顯示 git 基本信息

安裝完 git 以後,在 /etc/bash_completion.d 目錄中會生成一個 git-prompt 文件:服務器

if [[ -e /usr/lib/git-core/git-sh-prompt ]]; then
        . /usr/lib/git-core/git-sh-prompt
fi

打開/usr/lib/git-core/git-sh-prompt,註釋裏面寫了完整的操做步驟:app

# To enable:
#
#    1) Copy this file to somewhere (e.g. ~/.git-prompt.sh).
#    2) Add the following line to your .bashrc/.zshrc:
#        source ~/.git-prompt.sh
#    3a) Change your PS1 to call __git_ps1 as
#        command-substitution:
#        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
#        the optional argument will be used as format string.
#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
#        with two parameters, <pre> and <post>, which are strings
#        you would put in $PS1 before and after the status string
#        generated by the git-prompt machinery.  e.g.
#        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
#          will show username, at-sign, host, colon, cwd, then
#          various status string, followed by dollar and SP, as
#          your prompt.
#        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
#          will show username, pipe, then various status string,
#          followed by colon, cwd, dollar and SP, as your prompt.
#        Optionally, you can supply a third argument with a printf
#        format string to finetune the output of the branch status
cp /usr/lib/git-core/git-sh-prompt .git-prompt.sh
source .git-prompt.sh
export PS1='${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\W$(__git_ps1 " (%s)")\[\e[0m\]\$ '
export PROMPT_COMMAND='__git_ps1 "\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\W\[\e[0m\]" "\$ "'

接下來還需賦值幾個 git 環境變量讓提示符顯示更多 git 狀態:

export GIT_PS1_SHOWDIRTYSTATE=true
export GIT_PS1_SHOWCOLORHINTS=true
export GIT_PS1_SHOWUNTRACKEDFILES=true
export GIT_PS1_SHOWUPSTREAM="auto"

git config --global alias.lg "log --color --graph --pretty=format:'%C(yellow)%h%Creset%C(cyan)%C(bold)%C(red)%d%Creset %s %C(green)[%cn] %Creset%C(cyan)[%cd]%Creset' --date=format-local:'%m-%d %H:%M'"

顯示效果:

5.2 多帳號配置

我有兩個 git 帳號,分別是 gitee 和 github,且分別擁有各自的 name、email 和 ssh-key,我須要:

Ⅰ、兩個帳號均可以使用各自的密鑰對免密碼訪問

生成密鑰對:

# ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1] [-N new_passphrase] [-C comment] [-f output_keyfile]

ssh-keygen -t rsa -C "github@youclk.com" -f ~/.ssh/github/id_rsa -N ""
ssh-keygen -t rsa -C "gitee@youclk.com" -f ~/.ssh/gitee/id_rsa -N ""

編輯~/.ssh/config

Host     github.com
        HostName github.com
        User git
        IdentityFile ~/.ssh/github/id_rsa
Host     gitee.com
        HostName gitee.com
        User git
        IdentityFile ~/.ssh/gitee/id_rsa

聯通測試:

Ⅱ、 到達各自的倉庫時自動切換用戶名和郵箱

爲了保證各倉庫可以以正確的用戶信息提交版本,須要取消全局的用戶設置(我不理解爲何 global 中的用戶信息要去覆蓋各倉庫的,反過來不是更好嗎)。

git config --global --unset user.name
git config --global --unset user.emal

實現自動切換能想到的方案有不少,我更傾向於去修改 .git-prompt.sh,在 __git_ps1 () 函數末尾處增長一段邏輯:

if [ -z `git config user.name` ] && [ -z `git config user.email` ]; then
        local git_remote=`git remote -v`
        if [[ $git_remote =~ "github" ]]; then
                `git config user.name "github" && git config user.email "github@youclk.com"`
        elif [[ $git_remote =~ "gitee" ]]; then
                `git config user.name "gitee" && git config user.email "gitee@youclk.com"`
        fi
fi

順帶多提一下,git 默認忽略文件大小寫,然而做爲輕微的強迫症患者,我必定要和遠程倉庫保持徹底一致:git config --global core.ignorecase false

6. 密鑰對管理

我可能會一次性建立n臺雲服務器組成一個個集羣,每一個集羣中有一個 leader 和 n 個 follower,follower 只是提供計算能力,它應該把本身全權交給 leader,那麼在 leader 上必須可以訪問全部的
follower。這時候統一密鑰對管理就很是有必要了,只須要一個私鑰就能夠訪問全部的服務器,其實上一節提到的 git 密鑰對也能夠一塊兒管理。本節展開的話其實就是一些腳本實現,因此統一交給下一節概括。

7. 自動配置腳本編寫

如今我須要思考的是如何使用一行命令來自動完成以上全部的配置。因爲配置中涉及到一些私鑰等銘感信息,因此腳本必須放置於 git 私有庫中,可是 ubuntu 初始化的時候並無安裝 git,因此還須要一個公有庫來放置初始腳本,職能是安裝 git 和訪問私有庫。最終我須要實現執行如下一行代碼就完成整個 ubuntu 環境的配置:

# bash -c "$(curl -fsSL https://gitee.com/youclk/auto-config-entry/raw/master/centos/startup.sh)"
bash -c "$(curl -fsSL https://gitee.com/youclk/entry/raw/master/ubuntu/setting.sh)"

初始的入口腳本比較簡單(安裝 git,下載私有庫並執行 python 腳本):

#!/bin/bash

apt update

# install git
if [ -z `which git` ]; then
        apt install git
    if [ ! $? -eq 0 ]; then exit 0; fi
fi

# switch path to .auto_config
if [ ! -d ~/.auto_config ]; then  
    mkdir ~/.auto_config 
    if [ ! $? -eq 0 ]; then exit 0; fi
fi 
cd ~/.auto_config

# clone tools project
if [ ! -d "tools" ]; then
    git clone https://gitee.com/youclk/tools.git
    if [ ! $? -eq 0 ]; then exit 0; fi
fi
cd tools/ubuntu

python3 setting.py

rm -r ~/.auto_config

如下是 python 部分的結構:

代碼比較簡單,都是一些讀寫文件和結合系統命令的操做(步驟和說明都寫在註釋中了,再也不贅述)。

setting.py

import os
import socket
import subprocess
import sys

sys.path.append('../')

from utility import host


def edit_hostname():
    """
    edit /etc/hostname and /etc/hosts
    """
    old_hostname = socket.gethostname()
    new_hostname = str.strip(input('please write a hostname:'))
    if new_hostname and old_hostname != new_hostname:
        subprocess.check_call(['hostname', new_hostname])

        hostname_dir = '/etc/hostname'
        hosts_dir = '/etc/hosts'

        # write hostname
        with open(hostname_dir, 'w') as f:
            f.write(new_hostname + '\n')

        # read hosts
        with open(hosts_dir, 'r') as f:
            hosts_lines = f.readlines()

        # write hosts
        with open(hosts_dir, 'w') as f:
            local_ip = host.get_local_ip()
            n = 0
            for i in range(0, len(hosts_lines)):
                if local_ip in hosts_lines[i]:
                    hosts_lines[i] = hosts_lines[i].replace(old_hostname, new_hostname)
                    n += 1
            if not n:
                hosts_lines.append('\n' + local_ip + '\t' + new_hostname + '\n')
            f.writelines(hosts_lines)


def copy_config_files():
    """
    configure git history readLine commandPrompt
    """
    subprocess.check_call('cp -r bash_script/. ~/.', shell=True)
    with open('/root/.bashrc', 'r+') as f:
        bashrc = f.read()
        if '.bashrc_pro' not in bashrc:
            f.write('\nsource ~/.bashrc_pro.sh\n')


def configure_ssh_key():
    # copy ssk_key
    subprocess.check_call('cp -r ssh_key/. ~/.ssh/.', shell=True)
    # chmod
    subprocess.check_call('chmod 400 ~/.ssh/*/id_rsa', shell=True)

    # configure git config
    github_config = '''
Host     github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/git/id_rsa
    '''
    gitee_config = '''
Host     gitee.com
    HostName gitee.com
    User git
    IdentityFile ~/.ssh/git/id_rsa
    '''

    if os.path.exists('/root/.ssh/config'):
        with open('/root/.ssh/config', 'r+') as f:
            git_config = f.read()
            if 'github.com' not in git_config:
                f.write(github_config)
            elif 'gitee.com' not in git_config:
                f.write(gitee_config)
    else:
        with open('/root/.ssh/config', 'w') as f:
            f.write(github_config + gitee_config)


if __name__ == '__main__':
    if os.getuid() == 0:
        edit_hostname()
        copy_config_files()
        configure_ssh_key()
        print('success')
    else:
        print('please switch user => root')

host.py(一些能夠公用的函數單獨抽離出來):

import socket


def get_local_ip():
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as net:
        net.connect(('8.8.8.8', 80))
        return net.getsockname()[0]

.bashrc_pro.sh

#!/bin/bash

# config git

source .git_prompt.sh
export PS1='${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\W$(__git_ps1 " (%s)")\[\e[0m\]\$ '

if [ "$(whoami)" == "root" ]; then
    ps1_symbol="#"
else
    ps1_symbol="$"
fi

export PROMPT_COMMAND='__git_ps1 "\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\W\[\e[0m\]" "$ps1_symbol "'

export GIT_PS1_SHOWDIRTYSTATE=true
export GIT_PS1_SHOWCOLORHINTS=true
export GIT_PS1_SHOWUNTRACKEDFILES=true
export GIT_PS1_SHOWUPSTREAM="auto"

# history

export HISTCONTROL=ignoreboth # ignoreboth=ignoredups:ignorespace
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTIGNORE='pwd:ls'

shopt -s histappend
export PROMPT_COMMAND="history -a; $PROMPT_COMMAND" # make sure all terminals save history

# alias

alias aliyun="ssh -i ~/.ssh/aliyun/id_rsa"

結語

終於剔除了一塊疙瘩,之後一拿到服務器就能夠愉快地玩耍了。固然,以上腳本只適合我我的的使用習慣,部分代碼邏輯比較粗暴,各位看官參考和多多點贊就好,切勿直接使用,如有更好的想法,歡迎留言。


個人公衆號《有刻》,咱們共同成長!

相關文章
相關標籤/搜索