の続き。
課題だったインストールスクリプトを書いてみました。
#!/usr/bin/env bash # file: install-script.bash set -ueo pipefail cd "$(dirname "$0")" CMDNAME=$(basename "$0") print_usage() { cat - << EOF usage: ${CMDNAME} (-n|-x) [options...] -n Executes dry run mode; don't actually do anything, just show what will be done. -x Executes install. This option must be specified if you want to install. -S <suffix> Specify backup suffix. If not given, BACKUP_SUFFIX is used; neither is given, '~' is used. This argument cannot be empty string. EOF } MODE= BACKUP_SUFFIX=${BACKUP_SUFFIX:-\~} quote_each_args() { for i in $(seq 1 $#); do if [[ $i -lt $# ]]; then printf '%q ' "${!i}" else printf '%q' "${!i}" fi done } print_dry_run_message() { echo -e "will exec '$*'" } print_executing_message() { echo -e "executing '$*'..." } run() { if [[ "${MODE}" == 'dry-run' ]]; then print_dry_run_message "$(quote_each_args "$@")" else print_executing_message "$(quote_each_args "$@")" "$@" echo 'done.' fi } while getopts 'nxoS:u:m:' opt; do case $opt in n) MODE='dry-run' ;; x) MODE='execute' ;; S) BACKUP_SUFFIX="$OPTARG" ;; *) print_usage >&2 exit 1 ;; esac done if [[ -z "${MODE}" || -z "${BACKUP_SUFFIX}" ]]; then print_usage >&2 exit 1 fi # init submodules run git submodule update --init # create symbolic links echo ./install-script-tools/ls-linking-files.bash | while read -r file; do run ln -srvb -S "${BACKUP_SUFFIX}" -T "${file}" "${HOME}/.${file}" done # ここに残った初期化処理を書く( .gitconfig.local ファイルの作成とか vim の設定とか) # 今回は省略
#!/usr/bin/env bash # file: install-script-tools/ls-linking-files.bash set -ueo pipefail cd "$(dirname "$0")/.." # move to project root LINK_IGNORE=${LINK_IGNORE:-.linkignore} CMDNAME=$(basename "$0") print_usage() { cat - << EOF usage: ${CMDNAME} [options...] -v Verbose mode; print tracked files and ignored files. -h Print this message and exit. EOF } VERBOSE= while getopts 'vh' opt; do case $opt in v) VERBOSE='on' ;; h) print_usage exit ;; *) print_usage >&2 exit 1 ;; esac done remove_directory_contents() { cat - | sed 's/\/.*//g' | sort -u } TRACKED_FILES=$(git ls-files -c | grep -v '^\.') if [[ -n "${VERBOSE}" ]]; then echo "tracked files:" >&2 echo -e "${TRACKED_FILES}\n" >&2 fi IGNORED_FILES=$(git ls-files -ic --exclude-from="${LINK_IGNORE}") if [[ -n "${VERBOSE}" ]]; then echo "ignored files:" >&2 echo -e "${IGNORED_FILES}\n" >&2 fi echo "${TRACKED_FILES}" | grep -vxFf <(echo "${IGNORED_FILES}") | remove_directory_contents
# file: .linkignore # install-script でリンクさせたくないファイル/ディレクトリをここに置く # 形式は .gitignore と一緒 # サブディレクトリ内のファイルは関係ないので、誤解なきよう原則として / 始まりで指定すること /README* /install-script*
使い方:
$ ./install-script.bash -n
と入力すると、実際に実行されるコマンドが表示される(実行はされない)。
問題ないようなら
$ ./install-script.bash -x
で実行。
この際、リンクされるファイルが既に存在していた場合にバックアップが(~/.zshrc~
のような名前で)作られるので、問題なくインストール出来ているようなら削除する。
また、バックアップから復元したい場合のために、以下のようなスクリプトも書いた:
#!/usr/bin/env bash # file: install-script-tools/restore-dotfiles-from-backup.bash set -ueo pipefail WORKDIR=$(pwd) cd "$(dirname "$0")" CMDNAME=$(basename "$0") print_usage() { cat - << EOF usage: ${CMDNAME} (-n|-x) [options...] [files...] -n Executes dry run mode; don't actually do anything, just show what will be done. -x Executes restoration. This option must be specified if you want to restore. -d Deletes given file if no backup found. -S <suffix> Specify backup suffix. If not given, BACKUP_SUFFIX is used; neither is given, '~' is used. This argument cannot be empty string. files Specify file paths to restore. If not given, files would be linked by install-script and ~/.gitconfig.local is restored. EOF } MODE= DELETE= BACKUP_SUFFIX=${BACKUP_SUFFIX:-\~} quote_each_args() { for i in $(seq 1 $#); do if [[ $i -lt $# ]]; then printf '%q ' "${!i}" else printf '%q' "${!i}" fi done } print_dry_run_message() { echo -e "will exec '$*'" } print_executing_message() { echo -e "executing '$*'..." } run() { if [[ "${MODE}" == 'dry-run' ]]; then print_dry_run_message "$(quote_each_args "$@")" else print_executing_message "$(quote_each_args "$@")" "$@" echo 'done.' fi } restore() { for file in "$@"; do local backup="${file}${BACKUP_SUFFIX}" if [[ -e "${backup}" ]]; then run rm -f "${file}" run mv -T "${backup}" "${file}" elif [[ -n "${DELETE}" ]]; then run rm -f "${file}" fi done } while getopts 'nxS:d' opt; do case $opt in n) MODE='dry-run' ;; x) MODE='execute' ;; S) BACKUP_SUFFIX="$OPTARG" ;; d) DELETE='on' ;; *) print_usage >&2 exit 1 ;; esac done if [[ -z "${MODE}" || -z "${BACKUP_SUFFIX}" ]]; then print_usage >&2 exit 1 fi shift $((OPTIND - 1)) if [[ $# -eq 0 ]]; then ./ls-linking-files.bash | while read -r filename; do restore "${HOME}/.${filename}" done else (cd "${WORKDIR}" && restore "$@") fi
$ ./install-script-tools/restore-dotfiles-from-backup.bash -x ~/.zshrc
のようにして復元できる(ファイル名指定を省略した場合はリンクされるもの全部が対象になる)。
注意点として、 Mac だと ln コマンドのオプションが違うので、このままでは使えない。
後で Mac 版も書くつもり(blog で公開するかどうかは置いといて)。
実際のコードはこちら:
参考にして頂ければ幸い。