shells
theory
Shell (works on) Kernal: Interface between hardware and software.
echo $0
: determine current shell
cat /etc/shells
: display all installed shells
shell scripting
Types of Linux shells - just stick to Bash: born again shell 🤣
- naming convention: scripts directory
- Chmod a(all)+x script-name
- format
- Define Shell(#! /bin/bash)
如果是通过 (sh) ./xxx.sh 执行的脚本,它会开启一个 subshell,因此脚本里的 cd 等不会影响到外界;但是如果通过 alias, 或者通过 dot . xx.sh
执行,就可以在当前 shell process 里执行。
子进程
本地变量与环境变量
本地变量=局部变量 | 环境变量 | |
---|---|---|
创建方法 | a=123 | export a=123 |
查看方式 | set | export/env |
是否可以由子进程继承 | 不可以 | 可以 |
export: It is used to mark variables and functions to be passed to child processes. Basically, a variable will be included in child process environments without affecting other environments. |
source v.s. sh: call sh
会 initiate a fork(subshell) that runs a new session of bash
or shell
. 而 source
(又称为 .
) load and execute a shell script into the current shell process,所以如果想在 shell 里各种 cd 的,要用 source.
常用命令
系统
https://blog.csdn.net/weixin_41010198/article/details/109166131
detect os: echo $OSTYPE
Detect centos or redhat or ubuntu: cat /etc/os-release
lscpu
文件
- # 号代表我是 root 用户,才能查看 root 家目录
cp # -r能复制目录 -p保留原文件的操作时间 -a保留权限和属主
ls -lh # 文件 size
tail -f ./nohup.out | grep -C 10 xxx # or -A 与 -B
# 通过 head 和 tail 可以看到日志的起始区间
wc -l # 文件有多少行 统计文件内容信息
docker logs 09a1f7650ba6 2>&1 |grep 'GET / HTTP' | grep 'Mozilla' |g rep -v ' 304 '|grep -v 'bot'|wc -l # 网页有效访问次数 去除多次访问和 bot
# 在 linux 上 打包和压缩是分开的
# 打包
tar cf /target文件名字.tar /要被打包的目录
# 压缩
gzip
# 打包且压缩
tar czf xxx.tar.gz /要被打包的目录
# -x 解压 -f file -C 要解压到的文件夹路径
tar -xf filename.tar.gz -C /home/user/files
cat /proc/cpuinfo
# 返回:Processor : ARMv7 Processor rev 10 (v7l)
# 输入内容
cat >file1.txt <<EOF # <<EOF 和 >file1.txt 顺序不限
> aaa
> bbb
> ccc
> EOF
# 追加内容
cat >>file1.txt <<EOF
> 111
> 222
> 333
> EOF
权限
-
The Permission Groups used are:
-
u – Owner
-
g – Group
-
o – Others
-
a – All users
The Permission Types that are used are:
- r – Read
- w – Write
- x – Execute
e.g. chmod a-rw file1** chmod a+rw file1**
number mode:
- r = 4
- w = 2
- x = 1
How clever!! 1(x) 2(w) 3(w+x) 4(r) 5(r+x) 6(r+w) 7(r+w+x) so..eg. chmod 740 file1
-
Chown user[:group] : change owner
-
Chmod: change modes
pipe
注意 echo prints all of its arguments, it does not read from stdin
. So pipe 要接上 cat 之类的。
查找
find ./home -name "*.ini"
find ./ -type f -name “*.log” | xargs grep –i “被查找的字符串” 文件名
上传
scp v.s. sftp: 安全性都基于 ssh,但前者更快一点,因为接收包的校验更宽松
scp -r dist/* root@xxx.xxx.xx.xx:/dist/
其他
文件在修改时,会生成 swp file, 如果此时另一用户也同时编辑,会收到提示。删除 swp file 后接触控制。
process
init
是最初的进程管理方式 e.g.sudo /etc/init.d/nginx start
service
是init
的另一种实现 e.g.service nginx start
systemd
则是一种取代initd
的解决方案 字母d是守护进程(daemon)的缩写。其中systemctl
是systemd
的主命令,用于管理系统以及服务 e.g.sudo systemctl start nginx.service
何谓 unit?Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)
systemctl daemon-reload
systemctl enable cri-docker.service
systemctl enable --now cri-docker.socket
systemctl status nginx.service
systemctl stop [servicename]
systemctl disable [servicename]
rm /etc/systemd/system/[servicename]
rm /etc/systemd/system/[servicename] # and symlinks that might be related
rm /usr/lib/systemd/system/[servicename]
rm /usr/lib/systemd/system/[servicename] # and symlinks that might be related
systemctl daemon-reload
systemctl reset-failed
配置 /ect/systemd/system/xxx.service
[Unit]
Description=Aliyun Assist
ConditionFileIsExecutable=/usr/local/share/aliyun-assist/2.2.3.421/aliyun-service
After=network-online.target
Wants=network-online.target
[Service]
StandardOutput=journal+console
StandardError=journal+console
StartLimitInterval=3600
StartLimitBurst=10
ExecStart=/usr/local/share/aliyun-assist/2.2.3.421/aliyun-service
[install]
...
查看日志:journalctl -xe
journalctl -xeu xxservice
-
确定进程与杀进程
-
netstat -an | grep 3307
netstat -anp |grep 22
// 迁移服务器时,ssh connection refused, 就需要确定原有的 ssh 是不是开在 22.一般线上服务器都不会开在 22 -
sudo lsof -i :5955
-
sudo kill -9 PID
catchup: 一定要 sudo! 不 sudo 的话,有些用 sudo 打开的进程就看不见了,比如 nginx!
并且这些 closed 或 established 的都不用管,只有一个 Listen!
-
ps -fe | grep frp
-f: full format -e: listing all running processes -
top 指令也好用的
top -c
-
nohup 与 执行脚本&
- 执行脚本&:使 job 在 background 执行,但退出 shell 后进程也会被杀掉(the shell will terminate the sub-command with the hangup signal
kill -SIGHUP <pid>
)。另外输出 stdout/stderr 会打印到当前 shell outout. -
nohup: 它会阻止这个事件,所以子进程不会被杀。另外输出会被 redirect 到 nohup.out 上去
-
查看进程占用性能
-
htop => 是top的human friendly版:PID USER PRI(优先级) NI VIRT(虚拟内存) RES(实际内存) CPU%及MEM%都是占用越多则越消耗
网络
netstat
netstat -natpc | grep -i "8088"
-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-p 显示建立相关链接的程序名
-c(continous) 持续执行
统计80端口连接数:netstat -nat|grep -i "80"|wc -l
journalctl
它默认保存 4G 大小的日志,可以设置时间定时清理:journalctl --vacuum-time=10d
其他设置方式:fedora - Can I remove files in /var/log/journal and /var/cache/abrt-di/usr? - Unix & Linux Stack Exchange
传输
scp ./xx lmh@192.168.2.102:local-server-of-parkinglot/;
// rm xx; 一般再跟着把本地文件删除掉
sftp xx@xx.xx.xx: # 键入 help / ? 可查看帮助
put -r . ./
request
wget 下载
常用 -q0- 其中 q 表示 silent,0表示输出到… - 表示当前 output,所以可以通过下列指令运行某条远端的指令:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
安装非 yum 应用程序的基本思路
# 选择 tar.gz 版本下载
wget xxx.tar.gz
# 解压到指定位置
# 对于可以直接运行的程序
# 将安装目录放入 path 即可 一般将可执行文件放在 /usr/local/bin 下
# export PATH=something:$PATH
# source ~/.bashrc
# 对于自带安装程序的程序:
# e.g. nvm https://blog.csdn.net/COCOLI_BK/article/details/122254593
# e.g. 新创建 /root/.nvm 并安装
mkdir -p /root/.nvm
tar -zxvf v0.39.0.tar.gz -C /root/.nvm
# 如果要配置该程序的启动选项
# 打开文件配置
vim ~/.bashrc
# 导入 ($HOME ==> /root)
# export NVM_DIR="$HOME/.nvm/nvm-0.39.0"
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
# [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
source ~/.bashrc
# 进入下载根目录 进行安装
./install.sh
注:
RedHat 系列(Redhat、Centos、Fedora等): yum
Debian 系列(Debian、Ubuntu):apt-get
curl
# 获取 ssl 到期信息
curl https://www.lianmeihu.com -vI --stderr - | grep "expire date"
用户权限
-
新增用户:
useradd -m +用户名
这样才会在 /home 下创建同名家目录(userdel --remove xxx) -
查看用户分组
id
: uid=1006(qiwan) gid=1007(qiwan) 组=1007(qiwan) -
su - user 切换用户
/etc/passwd
: username:password:User ID:Group ID:comment:home directory:shell
``` # 查看所有用户 getent passwd # 账号个数: cat /etc/passwd | wc -l # 查找 root 用户(管理员组账号): cat /etc/passwd | grep :0 # 删除用户 userdel user2 # 查看用户信息 id user4
# add group groupadd groups # add member to group gpasswd –a groupname username cat /etc/group ```
显示当前用户:echo "$USER"
后台执行命令
nohup 命令 &
crontab
crontab -l
列表
crontab -e
编辑
0 0 * * * /usr/bin/sh xxx.sh
30 6 * * * /usr/bin/sh xxx.sh
@reboot /usr/bin/sh /root/boot.sh # reboot后执行
vim
- I 进入当行开头的插入模式
- a 后面 A当行结尾的插入模式
- yy 复制整行 3yy复制多行 y$复制到结尾
- dd 剪切 d$ 剪切到结尾
- x 删除当前字符 r替换当前字符
- :set nu 显示文件行 17T 去某一行 g与G去开头与结尾
- 替换 某行 :s/原字符/目标字符 替换整个文件:%s/原字符/目标字符 最后加/g代表全部替换 :3,5s表示第三行到第五行进行设置
- vim 全局设置:vim /etc/vimrc 最后一行加 set nu可以让vim打开都显示行号
- 多行注释:Ctrl + v 进入块选择模式,然后移动光标选中你要注释的行,再按大写的I 进入行首插入模式输入注释符号如// 或#,输入完毕之后,按两下ESC
Vim 初次使用时需要设置中文解码: vim ~/.vimrc
set tabstop=4
set ic
set nu
set expandtab
set cindent shiftwidth=4
set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936
set termencoding=utf-8
set encoding=utf-8
注意 .vimrc 不是 shell 配置,所以不要去 source 它
杂
-
ansi color: 控制台为什么可以输出颜色呢?这是因为可以在语句前添加形如 "\033[32m” 之类的命令,是一种 ANSI escape code,专门用来控制终端光标的移动与显示,etc
-
内部命令与外部命令
- `set -v: The shell shall write its input to standard error as it is read
- 获取时间: Getting Yesterday's or Tomorrow's date with bash on Linux / Unix - nixCraft
date +"%d%m%y"
date --date='1 day'
date --date="yesterday" +"%d-%m-%y"
yday=$(date --date="yesterday" +"%Y-%m-%d")
echo "$yday"
案例分析
在 .bashrc 里设置完 alias 后,企图 source ~/.bashrc,报错「command not found: shopt」⇒ shopt 是 bash built in 的命令,而我在 iterm2 里用的是 zsh。所以正确的做法是把设置放到 zshrc 里。或者:
exec bash # 使用 bash
source ~/.bashrc
exec zsh
Man 时:命令 [选项option] [文件名…] 这里选项是扩充命令功能,而文件名才是参数。文件名…代表可以加多个文件(空格隔开)
磁盘占用
- 先获取整体磁盘的使用情况 disk-free:
df -h
- 再可以切到对应文件夹,获取最上级大文件情况 disk-usage:
du -sh *
其中 -s 代表合并展示du -s * | sort -n
排序展示 所以不能 -h
- 我们常见的占用大户
- docker *-log.json
- metabase: metabase.db.trace.db 也是日志文件,怒清40GB!!
- 通过
cat /dev/null > *-json.log
的方式来删除
判断哪个端口有服务
- 有 nc 用 nc(netcat):
nc -nz 127.0.0.1 8080; echo $?
- 无 nc:
timeout 2 bash -c "</dev/tcp/127.0.0.1/8090"; echo $?
// 都是 0 有连接,1 无连接
Test if remote TCP port is open from a shell script - Stack Overflow
bash - Efficiently test if a port is open on Linux? - Stack Overflow
use config file
最简单的是 source, 但 source 有风险,要谨慎使用
https://unix.stackexchange.com/questions/175648/use-config-file-for-my-shell-script
bash - Parse config file and pass parameters to another script - Unix & Linux Stack Exchange
文件全局替换
sed:
- -i
will change the original, and stands for “in-place.”
- s
is for substitute, so we can find and replace.
sed 's/,\(.*china\)/,Tomas_proxy.lt\/\1/' FileName > NewFile
# 原地替换insert 用 # 分隔更稳妥。用 / 可能会出现 too many slashes 的问题
sed -i 's#http://www.google.com#https://www.baidu.com#g' default.json
find . -type f -name config.xml -print0 | xargs -0 sed -i 's#CONFIG_ENV=shukedev#CONFIG_ENV=ztlydev-shuke#g'
find . -name "*.yaml" -print0 | xargs -0 sed -i 's#5000/ztlydev#5000/ztlydev-shuke#g'
find . -name "*.sh" -print0 | xargs -0 sed -i 's#curly#curl#g'
sed -i 's/password: "Um6bUP5uzHx2VQJJ3Key"/#password: "Um6bUP5uzHx2VQJJ3Key"/g'
替换文件夹下的所有 occurrences:
find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"
conditional
[[ 注意前后空格 ]]
- 注意 string 用 =, number 用 eq
How to Compare Numbers and Strings in Linux Shell Script
- 三目:
a = [[ Baeldung =~ .*e.* ]] && echo True || echo False
lmh_api_test=8090
#!/bin/bash
CFG_FILE="./port.conf"
source ./port.conf
current_port=$lmh_api_test
new_port=""
if [[ $lmh_api_test = "8090" ]]
then new_port="8091"
else new_port="8090"
fi
echo new port should be $new_port
#docker login --username=testuser --password=testpassword hub.lmhdev.com
#docker pull hub.lmhdev.com/lmh_api_test:latest
#docker run -d \
# -p 8090:8080 \
# --restart=always \
# -v "/logs/lmh_api_test:/logs" \
# -v "/opt/project/lmh_api_test/cmd:/cmd" \
# --add-host=host.docker.internal:host-gateway \
# --name lmh_api_test_$new_port \
# hub.lmhdev.com/lmh_api_test:latest
is_new_port_up=0
i=0
while [[ ($is_new_port_up -eq 0) && (i -le 10) ]]
do
i=$i+1
# echo $(timeout 2 bash -c "</dev/tcp/127.0.0.1/$new_port"; echo $?)
is_new_port_up=$([[ $(timeout 2 bash -c "</dev/tcp/127.0.0.1/$new_port"; echo $?) -eq 0 ]] && echo 1 || echo 0)
sleep 1
done
if [[ $is_new_port_up -eq 1 ]]
then
# write new port
echo $is_new_port_up
echo "lmh_api_test=${new_port}" > $CFG_FILE;
# nginx-reload
/etc/nginx/start.sh
# remove old service
docker stop lmh_api_test_$current_port && docker rm lmh_api_test_$current_port
echo 服务已启动;
else
echo 服务启动失败;
fi