常用命令

找出当前正在使用的 shell 类别

1
ps -p $$

样例结果

1
2
3
4
5
6
7
8
9
10
# 这个例子当前的shell是 bash
[root@zabbix ~]# ps -p $$
PID TTY TIME CMD
15719 pts/0 00:00:00 bash

# 这个例子当前的shell是 zsh
┌──(kali㉿kali)-[~]
└─$ ps -p $$
PID TTY TIME CMD
159553 pts/1 00:00:04 zsh

查看当前shell版本

1
使用对应shell名 --version

样例

1
2
3
4
5
6
7
8
9
# zsh的版本
┌──(kali㉿kali)-[~]
└─$ zsh --version
zsh 5.9 (x86_64-debian-linux-gnu)

# bash的版本
[root@zabbix ~]# bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

查看当前系统支持的 shell 类型

1
cat /etc/shells

shebang的写法

1
2
3
4
5
6
# 虽然shell的shebang有多种写法,但是通过参考权威文档,
# 以后我的shell,统一使用下面的这中写法


#!/bin/bash
#

时间

时间戳与指定时间格式互转

  • date +%s 可以得到UNIX的时间戳;

  • 将日期时间与时间戳互转:

    1
    date -d "2015-08-04 00:00:00" +%s     # 输出:1438617600
  • 时间戳转换为指定时间格式:(具体时间格式写法,查看data的帮助)

    1
    date -d @1438617600  "+%Y-%m-%d"    # 输出:2015-08-04
  • 如果需要得到指定日期的前后几天:

    1
    2
    3
    seconds=`date -d "2015-08-04 00:00:00" +%s`       # 得到时间戳
    seconds_new=`expr $seconds + 86400` # 加上一天的秒数86400
    date_new=`date -d @$seconds_new "+%Y-%m-%d"` # 获得指定日前加上一天的日前
  • get today,yesterday

    1
    2
    3
    today=$(date +%Y%m%d)  # 指定yyyyMMdd格式的日期

    yesterday=$(date -d "1 day ago" +%Y%m%d) # 生成昨天yyyyMMdd的日期

计算耗时

start_time=date +%s
end_time=date +%s
cost_time=$(( ${end_time} - ${start_time} ))

计算 –这里有问题,let不好

let a = “1 + 2”

Sed

  1. 使用sed匹配某一行并替换这一行的内容

    1
    2
    3
    4
    5
    6
    sed -i "/<要替换的内容>/c<新内容>" <文件>

    (注意/c<新内容>,这里有一个字母c)

    例如:
    sed -i "/BUILD_NUMBER=/cBUILD_NUMBER=$1" file
  2. 文件头插入字符串 ABCDE

    1
    2
    # 文件头即文件的第一行插入数据
    sed -i '1i\ABCDE' file.txt

    说明: -i ,表示直接修改文件,(不使用则会将修改后的内容输出到屏幕)。

  3. 查看文件中间一段,你可以使用sed命令,如:

    1
    2
    3
    4
    5
    6
    7
    取文件100~200行[100,200]的数据
    sed -n '100,200p' filename > xxx

    sed -n '3p' filename # 输出文件的第3行

    sed -n '2,5p' filename # 输出文件的第2到5行

  4. 删除文件中N行

    1
    2
    3
    4
    5
    sed '/xxx/d' filename  # 删除包含xxx的行

    sed '2d' filename # 删除第2行

    sed '$d' filename # 删除最后一行

Mv

批量mv文件到指定目录下

-t <执行目录>

1
2
3

例如:将查询出的sql文件批量mv到test_target目录下
mv -t test_target `find . -name "gen_sql_file_20200101_1595*.log" -type f`

Awk

获取最后一列

样例:

1
2
3
cat xxx | awk -F '-' '{print $NF}'

上面样例是对传过来的数据,按照 '-' 分割,打印最后一列。

Find

使用find命令查找指定路径下的文件时:
注意:
find <非软连接路径> … –没有问题,非软连接路径可以直接使用路径名,或者最后带有”/“路径符号,都可以匹配到
find <软连接路径>/ … –软连接路径需要添加“/” 路径符号,否则无法匹配到内容

修改linux日期时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
date -s "20091112 18:30:50" &&hwclock --systohc

date -s //设置当前时间,只有root权限才能设置,其他只能查看
date -s 20120523 //设置成20120523,这样会把具体时间设置成空00:00:00
date -s 01:01:01 //设置具体时间,不会对日期做更改
date -s "01:01:01 2012-05-23" //这样可以设置全部时间
date -s "01:01:01 20120523" //这样可以设置全部时间
date -s "2012-05-23 01:01:01" //这样可以设置全部时间
date -s "20120523 01:01:01" //这样可以设置全部时间

# 检查硬件时间,这是BIOS时间
hwclock --show

# 如果它与我们的系统时间不同,让它设置它,使其匹配。 使用下面提供的命令,BIOS将从上面设置的系统时间获取时间,并记住它。
hwclock --systohc

文件的三个时间

1
2
3
4
5
6
7
8
$ stat 1234
File: 1234
Size: 7630 Blocks: 8 IO Block: 65536 regular file
Device: b8239a14h/3089340948d Inode: 2533274791067408 Links: 1
Access: (0644/-rw-r--r--) Uid: (197609/ spoonli) Gid: (197121/ UNKNOWN)
Access: 2020-12-03 23:11:06.615512900 +0800
Modify: 2020-12-03 23:11:06.615512900 +0800
Change: 2020-12-03 23:11:06.615064400 +0800
  • 三个时间代表的含义:
    • access time:表示我们最后一次访问(仅仅是访问,没有改动)文件的时间
    • modify time:表示我们最后一次修改文件的时间
    • change time:表示我们最后一次对文件属性改变的时间,包括权限,大小,属性等等。

有以下三种情况:

  1. 当我们仅仅只是读取文件时,access time 改变,而modify,change time 不会改变
  2. 当修改文件时,access,modify,change time 都会跟着改变
  3. 当修改文件属性时,change time 改变,而access,modify time 不变

通过ls命令查看文件的时间属性

1
2
3
ls -lc filename 列出文件的 ctime (最后更改时间)
ls -lu filename 列出文件的 atime(最后存取时间)
ls -l filename 列出文件的 mtime (最后修改时间)

文件md5

MD5只与文件内容有关,只要文件内容不一样,得出来的MD5值完全不一样。就是文件内容差一个字符不一样,得出的MD5值也完全不一样。

修改文件名是不会改变文件的md5值的

1
2
3
4
5
6
7
$ cp metabase.db.trace.db 1234

$ md5sum 1234
ff92155dcaddac06332929dd1948c241 *1234

$ md5sum metabase.db.trace.db
ff92155dcaddac06332929dd1948c241 *metabase.db.trace.db

SCP

无需单独输入密码,远程传输文件
-i

1
2
3
4
使用当前主机root用户的id_rsa密钥, 将192.168.10.25主机下的文件传输到当前目录下

scp -i /root/.ssh/id_rsa root@192.168.10.25:/opt/lobster-import-2.7.3_ti528-dev.zip ./

指定端口号,使用 -P 大写的 P ,

语法

1
scp -P port file_name [user@]ip:dir_path
  • -P port ,port 为指定的端口号
  • user,远程主机的用户名
  • ip,远程主机 IP
  • dir_path,远程的一个 user 可以写入文件的目录

例如:

1
scp -P 2233 test.log root@172.17.216.53:/home/test/

rsync替换cp命令显示速度和进度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

rsync "远程同步"(remote sync)的意思

- 本地两个目录之间同步文件
- 本地与远程机器同步文件
- x,不支持远程与远程的同步

如果是本地目录间同步,可以代替`cp`与`mv`

rsync 的最大特点是增量传输,即会检查发送方和接收方已有的文件,仅传输有变动的部分(默认规则是文件大小或修改时间有变动)。


参数:

一般最常用的选项组合:-avzPr 来进行传输,

一般,-a参数可以替代-r,
- 不过不需要同步文件元信息,则只使用-r,否则即可使用-a

-P :--progress 显示同步的过程及传输时的进度等信息
- 这个参数可以实现传输文件时打印传输进度的功能

创建FTP用户

创建ftp用户和目录

1
2
3
4
5
useradd lzy_datarefine --home-dir /data/DJ/datarefine/120000
usermod --home /data/DJ/datarefine/120000 -m lzy_datarefine
echo 123456 | passwd lzy_datarefine --stdin
su - lzy_datarefine
mkdir datarefine
1
2
3
4
[root@datarefine01 ~]# useradd lzy_datarefine --home-dir /data/DJ/datarefine/120000
useradd: cannot create directory /data/DJ/datarefine/120000
- 原因: 因为主目录所在的父目录不存在,或者主目录已经存在
- 我的情况是主目录所在父目录不存在,先创建父目录,然后再执行就可以了

lftp命令,linux登录ftp

1
2
3
4
lftp 用户名:密码@ftp地址:传送端口 (默认21)

例如:
lftp simba_dw:123456f@192.168.10.129

Linux内置命令

EXEC

并行

  1. 添加&让命令异步执行
    1
    2
    3
    4
    command &

    例如:
    sleep 10 &
    执行这条命令后,并不会阻塞10秒,而是立即返回。

当一个程序正在执行并且占用当前终端时我们同时按下 Ctrl + z ,这样就会把正在执行的前台程序放到后台挂起。

  1. 如果command2需要等到并行的command1执行之后再执行,就需要wait命令

    1
    2
    3
    command1 &    # command1命令会被放到后台执行
    wait # wait后面的命令command2会等待command1命令执行之后再执行
    command2
  2. 为了避免并发进程数过多,导致机器卡死,需要限制并发的数量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #!/bin/bash

    while read line
    do
    {
    joblist=($(jobs -p))
    while (( ${#joblist[*]} >= 20 ))
    do
    {
    sleep 1
    joblist=($(jobs -p))
    }
    done
    sleep 10 &
    }
    done

第二种执行并发程序的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
# 并发运行的最佳实践

# 总进程数
Sp=15
# 并发数,并发数过大可能造成系统崩溃
Qp=5
# 存放进程的队列
Qarr=();
# 运行进程数
run=0
# 将进程的添加到队列里的函数
function push() {
Qarr=(${Qarr[@]} $1)
run=${#Qarr[@]}
}
# 检测队列里的进程是否运行完毕
function check() {
oldQ=(${Qarr[@]})
Qarr=()
for p in "${oldQ[@]}";do
if [[ -d "/proc/$p" ]];then
Qarr=(${Qarr[@]} $p)
fi
done
run=${#Qarr[@]}
}

# main
for((i=0; i<$Sp; i++));do
echo "running $i "
sleep 3 &
push $!
while [[ $run -gt $Qp ]];do
check
sleep 0.1
done
done
echo -e "time-consuming: $SECONDS seconds" #显示脚本执行耗时

字符串比较

  1. 需要注意的是 [] 与操作数之间一定要有一个空格,否则会报错。

  2. 在进行字符串比较时,最好使用双中括号 [[ ]]. 因为单中括号可能会导致一些错误,因此最好避开它们。

    1
    2
    3
    4
    5
    6
    在string字符串比较时,更推荐使用 [[ $str1 == $str2 ]],
    尽管"=="与 [[ $str1 = $str2 ]] 效果一样,但是,"="可能会与赋值语句混淆

    [[ -z $str1 ]] 如果 str1 是空字符串,则返回真

    [[ -n $str1 ]] 如果 str1 是非空字符串,则返回真

    注意: = 号 前后有一个空格,有空格是关系比较,没有空格就变成了赋值效果

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Do this:
if [[ "${my_var}" == "some_string" ]]; then
do_something
fi

# -z (string length is zero) and -n (string length is not zero) are
# preferred over testing for an empty string
if [[ -z "${my_var}" ]]; then
do_something
fi

# This is OK (ensure quotes on the empty side), but not preferred:
if [[ "${my_var}" == "" ]]; then
do_something
fi

:当做string空值判断时,用上面的例子,不要用下面的例子
不好的例子:(不建议)

1
2
3
4
5
# Not this: 这种使用填充字符的方式不好
if [[ "${my_var}X" == "some_stringX" ]]; then
do_something
fi

  1. 可以通过 -a (and) 或 -o (or) 结合多个条件进行测试:

    1
    2
    [ $var1 -ne 0 -a $var2 -gt 2 ]  # 使用逻辑与 -a
    [ $var1 -ne 0 -o $var2 -gt 2 ] # 使用逻辑或 -o
  2. 使用逻辑运算符 && 和 || 可以轻松地将多个条件组合起来

    1
    if [[ -n $str1 ]] && [[ -z $str2 ]];

数值比较

结论:

  1. 不要将><等比较符号,与[[ ... ]]搭配来做数值比较,因为可能变成字典比较
  2. 建议使用(( ... ))搭配><等比价符号
  3. 或者 将-lt,-gt[[ ... ]]搭配来做数值比较

举例:

1
2
3
4
5
6
7
if (( $my_var > 3 )); then
do_something
fi

if [[ "${my_var}" -gt 3 ]]; then
do_something
fi

错误例子:

1
2
3
4
5
# Probably unintended lexicographical comparison.
if [[ "${my_var}" > 3 ]]; then
# True for 4, false for 22.
do_something
fi

shell算术操作

注意: 埋坑点,let aa=”03600”;echo $? =1, ((aa=03600));echo $?=1, aa$((0*3600));echo $?=0

TODO(SLi): 上面的shell算术部分写的有问题,需要修改一下


使用Trap捕获signals(信号量)

1. 主要参考:

Catching signals
还有Mark K Cowan’s answer

2.主要目的:

在脚本退出前做一些操作,可以是异常处理,可以是清理工作等

3. 语法及用法:

1
2
3
trap [COMMANDS] [SIGNALS]

your_code_do_some_thing

关于SIGNALS的知识点,参考Signals章节。

  • 注意:SIGKILL and SIGSTOP can not be caught, blocked or ignored.
    • 可以理解为,就是当你执行kill -9 PID的时候,程序会直接被杀掉,不会其他机会了。

4. 样例:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
# Here is a very simple example, catching Ctrl+C from the user, upon which a message is printed. When you try to kill this program without specifying the KILL signal, nothing will happen:
# 主要测试,捕获用户输入的Ctrl+C的快捷键,并打印信息。如果你使用kill命令来直接杀死这个脚本,那么什么都不会输出

trap "echo Booh!" SIGINT SIGTERM
echo "pid is $$"

while : # This is the same as "while true".
do
sleep 60 # This script is not really doing anything.
done

新手可能常犯的错误

1. 引用变量时,关于引号的使用

引用变量时需要在变量名前添加”$”前缀,但是是否使用引号,以及如何使用,可能会带来不同的结果

1
2
3
4
5
6
7
8
9
10
# 定义变量如下:

example="Hello world"


主要注意,不使用引号,使用双引号的区别

使用方式 结果 单词数
$example Hello world 2
"$example" Hello world 1

在下面两个地方使用内容中有空格的变量时尤其需要注意:

  • 在脚本中引用外部配置文件的时候尤其需要注意上面的内容,
  • 在for循环中使用时需要注意
    1
    2
    3
    4
    5
    6
    7
    for animal in "$example"; do
    ... # 这里就只会循环一次
    done

    for animal in $example; do
    ... # 这里就会循环两次
    done
    上面的内容引用Expanding (using) variablesString lists in for-loops 两个章节

总的规则就是

  1. 引用变量时,总是用双引号(“”)将应用的变量包起来,而不是裸用$符号
    • 数组除外,只有在使用数组 时,不需要用双引号包起来
    • 如果是$数字的形式,那么当超过9的时候,需要使用例如,${11}的形式,否则$11则是$1和数字1的组合,并不是第11个参数
      1
      2
      3
      4
      5
      6
      例如:

      PRG="$0"

      PRGDIR=`dirname "$PRG"`

后台运行

screen 命令

  1. 开启一个新的session
  2. 查看开启的session
  3. 临时离开当前session
  4. 进入指定session
  5. 关闭(销毁)session
  6. 新session自定义名称
  • screen -S <新session名称>

& 和 nohup

数组

总结常用数组的内容

更多内容可以参考 Shell 数组

定义

定义方式1

Shell 数组用括号()来定义,元素用 空格 符号分割,语法格式如下:

1
array_name=(value1,value2,...)

例如:

1
tags=("A" "B" C D)

定义方式2

直接使用数组下标定义数组:

1
2
3
4
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
...

取值

获取单个元素

读取数组元素值的一般格式:

1
${array_name[index]}

例如:

1
2
3
4
5
my_arr=(a b c d)

echo "first element: ${my_arr[0]}"
echo "second element: ${my_arr[1]}"
...

获取数组中的所有元素

格式如下:

1
2
3
4
# 使用 @,或者 * 
${array_name[@]}

${array_name[*]}

例如:

1
2
3
4
my_arr=(a b c d)

echo "all elements: ${my_arr[*]}"
echo "all elements: ${my_arr[@]}"

获取数组索引

在数组变量名前添加 !感叹号,可以获取素组的所有索引,格式如下:

1
2
3
${!array_name[*]}

${!array_name[@]}

获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,在数组变量名前添加 #符号, 格式如下:

1
2
3
${#array_name[*]}

${#array_name[@]}

1. for 循环遍历数组

注意点:

  1. 数组要单独定义,不能定义在 for 的 列表位置;

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a=(1 2 3)

1. for .. in 的形式
for i in ${a[@]};do
echo $i
done

2. 可以带数组下标的 for...in
for i in "${!a[@]}";do
printf "%s\t%s\n" "$i" "${a[$i]}"
done

3. 标准 for 循环
for(( i=0;i<${#a[@]};i++ ));do
echo ${a[i]}
done

2. for … 序列

序列的形式可以直接写在 <列表> 的位置;

1
2
3
for i in {22..30};do
echo $i;
done

Set

-e 和 -E 作用的解释,不过-E还不懂,得测试一下才行

1
2
3
4
-e      Exit immediately if a pipeline (which may consist of a single simple command),  a subshell command enclosed  in  parentheses,  or  one  of the commands executed as part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with a non-zero status.  The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or ││ list except the command following the final && or ││, any command in a pipeline but the last, or  if  the command’s  return  value  is  being  inverted with !.  A trap on ERR, if set, is executed before the shell exits.  This option applies to the shell environment and each subshell environment separately  (see  COMMAND  EXECUTION  ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.

-E If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.

修改系统的sysctl vm.swappiness=0值

1
2
3
cat /proc/sys/vm/swappiness
sysctl vm.swappiness=0
each "vm.swappiness=0" >> /etc/sysctl.conf

理解Linux系统/etc/init.d目录和/etc/rc.local脚本

  1. /etc/init.d 该目录下包含许多系统的各种服务的启动和停止脚本;

    • 用service命令可执行init.d目录中相应服务的脚本。

    • 例如:/etc/init.d/network restart 重启网络, service network restart

  2. /etc/rc#.d(其实是软链接文件,/etc/rc.d/rc#.d,这里#代表一个指定的初始化级别,范围是0~6),该文件下包含许多对进程进行控制的脚本。这些脚本放置的地方,将决定这些脚本什么时候开始运行。

  3. /etc/rc.local(其实是软连接文件:/etc/rc.d/rc.local),该脚本是在系统初始化级别脚本运行之后执行的;

    该脚本是在系统初始化级别的脚本运行之后再执行的,因此可以安全地在里面添加你想在系统启动之后执行的脚本。

查看内存型号

打开命令窗口Ctrl + Alt + T输入:

1
sudo dmidecode --type memory

即可显示内存条的信息。

开机自启

centos7

1
2
3
4
5
6
systemctl list-unit-files  (查看开机启动项)

systemctl list-unit-files | grep 程序名称 (查看某些服务开机启动状态)

systemctl list-unit-files | grep enable (查看哪些为开机启动服务)

centos6

1
ckconfig --list

Shell 判断字符串包含关系

方法一,利用 grep 查询

1
2
3
4
5
6
7
8
dirp="学习笔记/运维/ELK"
result=$(echo $dirp| grep "学习笔记")
# 判断结果是否为空字符串,不为空即包含,为空则不包含;
if [[ -n $result ]];then
echo "包含"
else
echo "不包含"
fi

方法二,利用字符串运算符

1
2
3
4
5
6
dirp="学习笔记/运维/ELK"
if [[ $dirp =~ "学习笔记" ]];then
echo "包含"
else
echo "不包含"
fi

利用 =~ 直接判断字符串的包含,结果为 true 则包含,为 false 则不包含。

bash 如何数组和字符串相互转换

如何把下面的数组和字符串进行相互转换?

1
2
3
4
("学习笔记" "运维" "ELK") <=> "学习笔记/运维/ELK"

tag_arr=("学习笔记" "运维" "ELK")
tag_str="学习笔记/运维/ELK"

数组->字符串

1
2
3
4
DEFAULT_IFS=$IFS
# 将数组 -> 字符串
tag_str=[$(IFS=,;echo "${tag_arr[*]}")]
IFS=$DEFAULT_IFS

通过赋值IFS,指定分隔符/连接符。

注意,处理完后,需要恢复 IFS 的默认值,防止后续处理出现问题。

如果连接符不需要特定(默认为 空格,回车,Tab),那么数组->字符串只需要将 IFS部分去掉即可

1
tag_str=[$(echo "${tag_arr[*]}")]

字符串-> 数组

方法一:如果字符串是空格分隔,直接使用下面命令即可

1
2
tag_str="学习笔记 运维 ELK"
tag_arr=(${tag_str})

方法二:如果字符串分隔符是其他字符,那么把分隔符变成空格即可。

1
2
tag_str="学习笔记/运维/ELK"
tag_arr=($(echo $tag_str | tr "/" " "))

方法三:使用 awk 命令分割字符串,然后拼接为数组。

1
tag_arr=($(echo $tag_str | awk '{len=split($0,a,"/");for(i=2;i<=len;i++) print a[i]}'))

参考链接:bash如何把数组和字符串互相转换

lsmod

lsmod 显示/列出 Linux 内核中当前加载的 modules 的状态。该命令将 /proc/modules 文件的内容进行格式化输出。

字段含义

  • Module:模块的名称。这通常是模块文件的名称,减去扩展名(.o或.ko),但它可能有一个自定义名称,可以在使用insmod命令插入模块时将其指定为选项。
  • Size:驻留模块使用的内存量,以字节为单位。
  • Used by:此列包含一个数字,表示正在使用的模块实例数。 如果该数字为零,则当前未使用该模块。 数字后面的文本表示有关使用模块的内容的任何可用信息:这通常是设备名称,文件系统标识符或另一个模块的名称。

参考链接:lsmod命令

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信