Skip to content

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
  • serviceinit 的另一种实现 e.g. service nginx start
  • systemd 则是一种取代 initd 的解决方案 字母d是守护进程(daemon)的缩写。其中 systemctlsystemd 的主命令,用于管理系统以及服务 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

  • 内部命令与外部命令

image-20220915173048910

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