运维
从事运维工作多年,一直在一线进行救火工作。你是否还记得服务器出现问题CTO站在你身后时你疯狂输出的样子和心情。后续我会跟大家分享一线运维必备的一些基础命令,希望阅读之后大家可以把这些手法融入到你的血液里,让你遇到问题的时候各种命令都可以信手拈来。
控制台使用技巧
操作快捷键:
- 使用ctrl+r寻找你使用过的命令,直接输入你想要的命令行。(请各位不要把常用命令复制到一个txt里面,想到哪个的时候过去复制一下,实在是看不下去了。)在这里我分享一个bash脚本供大家保存命令(安全问题自行考虑),请将该脚本放置到~/.bashrc,自行引用,引用完毕后输入h直接展示历史记录。(不兼容zsh,zsh用户慎用)
# history
# persistent shell history with advanced search
#
# Copyright (C) 2013 Mara Kim, Kris McGary
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see http://www.gnu.org/licenses/.
### USAGE ###
# Source this file in your shell's .*rc file
### SETTINGS ###
if [ -z "$ALL_HISTORY_FILE" ]
then
ALL_HISTORY_FILE=~/.bash_all_history
fi
### END SETTINGS ###
#set up history logging of commands
export HISTTIMEFORMAT=' %F %T '
export HISTCONTROL='ignorespace'
PROMPT_COMMAND="_log_history; ${PROMPT_COMMAND}"
_HISTNUM=""
_LAST_COMMAND=""
declare -a _PWD
# logging function
function _log_history {
local directory="$(pwd -P)"
local histnum="$(history 1 | sed 's/ *\([0-9]*\).*/\1/')"
if [ -z "$_HISTNUM" ]
then
_HISTNUM="$histnum"
elif [ "$histnum" != "$_HISTNUM" ]
then
if [ "$directory" != "$_PWD" ]
then
local match
local i
for i in {1..8}
do if [ "$directory" = "${_PWD[$i]}" ]
then unset _PWD[$i]
match="true"
fi
done
if [ -z "$match" ]
then unset _PWD[9]
fi
_PWD=( "$directory" "${_PWD[@]}" )
local directory="${_PWD[1]}"
fi
local command="$(cat <(history 1 | head -1 | sed 's/[^ ]* //') <(history 1 | tail -n +2))"
printf '%q\t%q\t%s\n\x00' "$USER@$HOSTNAME" "$directory" "$command" >> "$ALL_HISTORY_FILE"
if [ "$_LAST_COMMAND" = "$command" ]
then
history -d "$histnum"
else
_HISTNUM="$histnum"
_LAST_COMMAND="$command"
fi
fi
}
# history
h () {
gawk_history_interactive "/" 1 "$@"
}
# history of commands run in this directory and subdirectories (with grep)
dh () {
gawk_history_interactive "$(printf '%b' "$(pwd -P)")" 1 "$@"
}
# history of commands run in this directory only (with grep)
ldh () {
gawk_history_interactive "$(printf '%b' "$(pwd -P)")" 0 "$@"
}
# select from history
h! () {
select_history "/" 1 "$@"
}
# select from history
dh! () {
select_history "$(printf '%b' "$(pwd -P)")" 1 "$@"
}
# select from history
ldh! () {
select_history "$(printf '%b' "$(pwd -P)")" 0 "$@"
}
# select from working directory history
cd! () {
local histline
local history
local item
local line
if [ -z "$*" ]
then history=( "${_PWD[@]}" )
else while read -r -d '' histline
do history+=( "$histline" )
done < <( gawk_directory_history "/" 1 "$@" )
fi
select item in "${history[@]}"
do
if [ -z "$item" ]
then break
fi
# Read user edited command
read -er -i "$item" -p '$ ' item
while bash -n <<<$item 2>&1 | grep 'unexpected end of file' > /dev/null || [ -z "${item%%*\\}" ]
do
read -r -p '> ' line
item="$item"$'\n'"$line"
done
# Add command to history and run
history -s "cd $item"
cd "$item"
return $?
done
}
# bash completions
complete -cf h
complete -cf dh
complete -cf ldh
complete -cf h!
complete -cf dh!
complete -cf ldh!
# select history implementation
select_history () {
local histline
local history
local item
local line
while \read -r -d '' histline
do
history+=( "$histline" )
done < <( gawk_history "$@" |
gawk 'BEGIN { RS="\0"; FS="\t"; }
{ for(i = 5; i <= NF; i++) $4 = $4 "\t" $i}
{ a[$4] = NR }
END { PROCINFO["sorted_in"] = "@val_num_desc";
num = 0;
for (i in a) { printf "%s\0", i; num++; if (num == 10) break } }' )
select item in "${history[@]}"
do
if [ -z "$item" ]
then break
fi
# Read user edited command
read -er -i "$item" -p '$ ' item
while bash -n <<<$item 2>&1 | grep 'unexpected end of file' > /dev/null || [ -z "${item%%*\\}" ]
do
read -r -p '> ' line
item="$item"$'\n'"$line"
done
# Add command to history and run
history -s "$item"
eval "$item"
return $?
done
}
# directory history implementation
gawk_directory_history () {
gawk_history "$@" |
gawk 'BEGIN { RS="\0"; FS="\t"; }
{ a[$2] = NR }
END { PROCINFO["sorted_in"] = "@val_num_desc";
num = 0;
for (i in a) { printf "%s\0", i; num++; if (num == 10) break } }'
}
# interative gawk history implementation
gawk_history_interactive () {
# read arguments
local state="first"
for arg in "$@"
do
if [ "$state" = "first" ]
then
state="second"
elif [ "$state" = "second" ]
then
state=""
elif [ "$arg" = "-h" -o "$arg" = "--help" ]
then
printf 'Usage: [[l]d]h[!] [CONTEXT] [TIMESPEC] [--] [SEARCH]
Search command history.
SEARCH is a regular expression understood by `gawk`
used to match the executed command.
TIMESPEC is an argument of the form "[START..END]",
where START and END are strings understood by `date`.
A single day may be specified by "[DATE]".
CONTEXT is an argument of the form "USER@HOST:DIRECTORY"
or "USER@HOST::DIRECTORY", where each field is optional.
"@" is used to specify user or host filters.
":" is used to specify a directory filter.
"::" may be used instead to exclude subdirectories.
Select from the 10 most recent matching entries
adding `!` to the command (ex. `h!`).
The selected command may be edited before execution.
'
return 0
fi
done
gawk_history "$@" | tr -d '\000' | less -FX +G
}
# core gawk history implementation
gawk_history () {
# read arguments
local state="first"
local search
local timespec
local user
local host
local argdir
local directory
local recursive_dir
for arg in "$@"
do
if [ "$state" = "first" ]
then
directory="$arg"
state="second"
elif [ "$state" = "second" ]
then
recursive_dir="$arg"
state=""
elif [ "$state" = "input" ]
then
search+="${arg}.*"
elif [ "$state" = "time" ]
then
timespec="$timespec $arg"
if [ -z "${arg/*]/}" ]
then state=""
fi
elif [ "$arg" = "--" ]
then state="input"
elif [ -z "${arg/\[*/}" -a ! "$timespec" ]
then
timespec="$arg"
if [ "${arg/*]/}" ]
then state="time"
fi
elif [ -z "${arg/*@*/}" -o -z "${arg/*:*/}" ]
then
if [ -z "${arg/*@*/}" ]
then
if [ "${arg%%@*}" -a ! "$user" ]
then
user="${arg%%@*}"
fi
if [ "${arg#*@}" -a ! "$host" ]
then
host="${arg#*@}"
host="${host%%:*}"
fi
fi
if [ -z "${arg/*::*/}" ]
then
if [ "${arg#*::}" -a ! "$argdir" ]
then
argdir="${arg#*::}"
recursive_dir=0
fi
elif [ -z "${arg/*:*/}" ]
then
if [ "${arg#*:}" -a ! "$argdir" ]
then
argdir="${arg#*:}"
recursive_dir=1
fi
fi
else
search+="${arg}.*"
fi
done
if [ "$argdir" ]
then
if [ -z "${argdir##~*}" ]
then directory="$(readlink -m -- "$HOME${argdir#\~}")"
else directory="$(readlink -m -- "$argdir")"
fi
fi
local start_time
local end_time
if [ "${timespec/*..*/}" ]
then
timespec="${timespec#[}"
timespec="$(date -d "${timespec%]}" '+%F')"
start_time="$timespec"
end_time="$timespec + 1day"
else
start_time="${timespec%..*}"
start_time="${start_time#[}"
end_time="${timespec#*..}"
end_time="${end_time%]}"
fi
if [ "$start_time" ]
then
start_time="$(date -d "$start_time" '+%F %T')"
if [ -z "$start_time" ]
then
return 1
fi
fi
if [ "$end_time" ]
then
end_time="$(date -d "$end_time" '+%F %T')"
if [ -z "$end_time" ]
then
return 1
fi
fi
if [ "$recursive_dir" = 0 ]
then gawk -vdirectory="$directory" -vstart_time="$start_time" -vend_time="$end_time" -vsearch="$search" -vhost="$host" -vuser="$user" \
'BEGIN { RS="\0"; FS="\t"; user_matcher="^"user"(@|$)"; host_matcher="[^@]*@"host;}
{ for(i = 5; i <= NF; i++) $4 = $4 "\t" $i}
index($2,directory) == 1 && length($2) == length(directory) {
if((length(start_time) == 0 || $3 >= start_time) &&
(length(end_time) == 0 || $3 <= end_time) &&
(length(user) == 0 || $1 ~ user_matcher ) &&
(length(host) == 0 || $1 ~ host_matcher ) &&
(length(search) == 0 || $4 ~ search )) printf "%s\t%s\t%s\t%s\0", $1,$2,$3,$4}' "$ALL_HISTORY_FILE"
elif [ "$directory" = "/" ]
then gawk -vdirectory="$directory" -vstart_time="$start_time" -vend_time="$end_time" -vsearch="$search" -vhost="$host" -vuser="$user" \
'BEGIN { RS="\0"; FS="\t"; user_matcher="^"user"(@|$)"; host_matcher="[^@]*@"host;}
{ for(i = 5; i <= NF; i++) $4 = $4 "\t" $i}
{ if((length(start_time) == 0 || $3 >= start_time) &&
(length(end_time) == 0 || $3 <= end_time) &&
(length(user) == 0 || $1 ~ user_matcher ) &&
(length(host) == 0 || $1 ~ host_matcher ) &&
(length(search) == 0 || $4 ~ search )) printf "%s\t%s\t%s\t%s\0", $1,$2,$3,$4}' "$ALL_HISTORY_FILE"
else gawk -vdirectory="$directory" -vstart_time="$start_time" -vend_time="$end_time" -vsearch="$search" -vhost="$host" -vuser="$user" \
'BEGIN { RS="\0"; FS="\t"; user_matcher="^"user"(@|$)"; host_matcher="[^@]*@"host;}
{ for(i = 5; i <= NF; i++) $4 = $4 "\t" $i}
index($2,directory) == 1 {
if((length(start_time) == 0 || $3 >= start_time) &&
(length(end_time) == 0 || $3 <= end_time) &&
(length(user) == 0 || $1 ~ user_matcher ) &&
(length(host) == 0 || $1 ~ host_matcher ) &&
(length(search) == 0 || $4 ~ search )) printf "%s\t%s\t%s\t%s\0", $1,$2,$3,$4}' "$ALL_HISTORY_FILE"
fi
}
_init_log_history () {
local histline
while read -r -d '' histline
do _PWD+=( "$histline" )
done < <( gawk 'BEGIN { RS="\0"; FS="\t"; }
{ a[$2] = NR }
END { PROCINFO["sorted_in"] = "@val_num_desc";
num = 0;
for (i in a) { printf "%s\0", i; num++; if (num == 10) break } }' \
"$ALL_HISTORY_FILE" )
local directory="$(pwd -P)"
if [ "$directory" != "$_PWD" ]
then
local match
local i
for i in {1..8}
do if [ "$directory" = "${_PWD[$i]}" ]
then unset _PWD[$i]
match="true"
fi
done
if [ -z "$match" ]
then unset _PWD[9]
fi
_PWD=( "$directory" "${_PWD[@]}" )
local directory="${_PWD[1]}"
fi
}
_init_log_history
- 熟练掌握分屏技巧Mac用户自不必说,iTerm分屏:
⌘ + d: 垂直分屏。
⌘ + shift + d: 水平分屏。
⌘ + ]和⌘ + [在最近使用的分屏直接切换。
⌘ + opt + 方向键切换到指定位置的分屏。
会了这些之后还有一个比较关键的就是多个窗口同时输入命令:alt + shift + i 这样右上角会显示一个图标,此时多个会话会同时输入命令。(包括不同tab)
Windows用户必须熟练掌握Tmux,具体教程网上一搜一堆。我这里就提一句打开Tmux的时候上传下载要小心。 - 熟练掌握光标移动命令
如果你已经干了1-2年运维。登录服务器的时候还是在通过左右方向键在移动光标的话,真是应该反思了,在这里分享一张示意图。该示意图生动的描述了如何摆脱方向键实现光标的快速移动。如果你使用的命令行工具不兼容Alt相关的快捷键的话请自行实现键盘映射,比如我使用hhkb键盘把Alt-b和Alt-f改为fn-b和fn-f。
今天先聊到这里,后续会分享更多与各种场景相关的应对方案,让大家处理线上故障的时候更加得心应手。