Shell

关键字

local

local关键字用于声明一个局部变量,其作用域仅限于当前函数。这意味着这个变量只在当前函数内部可见和可用,并且不会影响外部作用域的同名变量。

local关键字的主要作用是:

  1. 避免变量污染: 在函数内部,您可能会使用与外部作用域相同名称的变量,但是通过使用local声明局部变量,可以确保函数内部的操作不会影响到外部同名变量。这有助于减少命名冲突和不必要的副作用。
  2. 作用域控制: local允许您在函数内部创建一个独立的作用域,使得函数内部的变量不会泄漏到全局作用域。这有助于编写更清晰和可维护的代码,因为您可以更容易地理解和跟踪变量的生命周期。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

# 全局变量
global_var="Global Variable"

my_function() {
# 声明局部变量,覆盖了全局同名变量
local global_var="Local Variable"
echo "Inside function: $global_var"
}

# 调用函数
my_function

# 打印全局变量,不受函数内部局部变量的影响
echo "Outside function: $global_var"

特殊参数变量

默认赋值

${1-}

1
2
3
# 使用${1-}来获取第一个参数,如果未定义或为空,则使用默认值"Default"
param=${1-Default}
echo "Parameter: $param"

执行结果:Parameter: Argument

${PYTHONHOME+_}

${PYTHONHOME+_}:这个表达式是一种参数扩展(parameter expansion)的形式。作用是检查PYTHONHOME是否已经定义。它会返回以下两种情况之一:

  • PYTHONHOME已经被定义,则返回_(一个非空字符串)
  • 如果PYTHONHOME未被定义,则返回空字符串

当前脚本的进程ID(PID)

1
$$

上一个命令执行结果

1
2
3
$?
# echo 结果0 成功
# echo 结果1 失败/异常

本脚本相对路径

1
$0

传入脚本/函数的第几个参数

1
2
3
# $1代表第一个、$2代表第二个、以此类推。。。
$1 $2 $3 ...

传入给脚本/函数所有参数的列表

$@:代表了传递给脚本的所有参数的列表。如果你的脚本需要处理多个参数,可以使用 $@ 来一次性获取所有参数。

1
2
3
4
# 打印所有$@包含的参数
for arg in "$@"; do
echo "Argument: $arg"
done

递给脚本/函数的参数数量

$#:表示传递给脚本的参数数量。如果你需要知道有多少个参数被传递给了你的脚本,或者需要进行基于参数数量的不同操作,就可以使用 $#

1
2
3
4
5
6
# 传入的参数数目小于2,打印Not enough arguments supplied.
if [ $# -lt 2 ]; then
echo "Not enough arguments supplied."
else
echo "Processing with arguments."
fi

运算符

> < = 需要在例如if (( $number > 5 ))(( ))使用

  • -z string: 判断字符串是否为空。
  • -n string: 判断字符串是否非空。
  • string1 = string2: 判断两个字符串是否相等。
  • string1 != string2: 判断两个字符串是否不相等。
  • int1 -eq int2: 判断两个整数是否相等。
  • int1 -ne int2: 判断两个整数是否不相等。
  • int1 -gt int2: 判断 int1 是否大于 int2,简写形式: <
  • int1 -lt int2: 判断 int1 是否小于 int2,简写形式: >
  • int1 -ge int2: 判断 int1 是否大于等于 int2。
  • int1 -le int2: 判断 int1 是否小于等于 int2。

逻辑运算符

  • &&(与运算符):用于判断多个条件都为真时,整个表达式为真。
  • ||(或运算符):用于判断多个条件中只要有一个为真,整个表达式为真。
  • !(非运算符):用于取反一个条件,如果原条件为真,则取反后为假,反之亦然。
  • -a:逻辑与。
  • -o:逻辑或。
  1. 使用 && 判断多个条件是否都满足:
1
2
3
if [ 条件1 ] && [ 条件2 ]; then
# 在条件1和条件2都为真时执行的操作
fi

示例:

1
2
3
if [ "$var1" -gt 10 ] && [ "$var2" -lt 20 ]; then
echo "var1大于10且var2小于20"
fi
  1. 使用 || 判断多个条件是否有一个满足:
1
2
3
if [ 条件1 ] || [ 条件2 ]; then
# 在条件1或条件2至少有一个为真时执行的操作
fi

示例:

1
2
3
if [ "$var1" -eq 5 ] || [ "$var2" -eq 10 ]; then
echo "var1等于5或var2等于10"
fi
  1. 使用 ! 对条件取反:
1
2
3
if ! [ 条件 ]; then
# 在条件为假时执行的操作
fi

示例:

1
2
3
if ! [ -e "$file" ]; then
echo "文件不存在"
fi

文件

  • -e file: 判断文件是否存在。
  • -f file: 判断文件是否存在且为普通文件。
  • -d file: 判断文件是否存在且为目录。
  • -r file: 判断文件是否存在且可读。
  • -w file: 判断文件是否存在且可写。
  • -x file: 判断文件是否存在且可执行

判断文件内容是否为空

1
2
3
if [ ! -s ./testfile ]; then
echo "文件为空";
fi

数组

创建数组

Shell 数组用括号来表示,元素用”空格”符号分割开

1
array_name=(value1 value2 ... valuen)

读取数组

index下标,写@代表所有元素,可用于打印所有元素,可用于循环遍历

1
${array_name[index]}

易混淆语法

$((...))

$((...))中的任何参数都不用带$,直接可进行基本运算得出结果赋值给新的变量或者作为新的变量使用

1
2
3
4
5
6
7
load_1min=$(cat /proc/loadavg | awk '{print $1 * 100}')
load_5min=$(cat /proc/loadavg | awk '{print $2 * 100}')
load_15min=$(cat /proc/loadavg | awk '{print $3 * 100}')

# 计算平均负载(整数)
combined_load=$((load_1min + load_5min + load_15min))
combined_load=$((combined_load / 3))

if

if 语句后面可以使用单中括号 [ ] 或双中括号 [[ ]],它们有一些区别

  1. 单中括号 [ ]
    • 单中括号是旧式的测试结构,仍然被支持,但不够灵活。
    • 单中括号要求在内部使用空格来分隔参数和运算符,例如 [ "$var" -eq 5 ]
    • 单中括号在某些情况下可能需要引号来防止意外的参数展开和拆分。
  2. 双中括号 [[ ]]
    • 双中括号是Bash中的扩展测试结构,通常比单中括号更强大和灵活。
    • 双中括号不需要在内部使用引号来保护变量,例如 [[ $var -eq 5 ]]
    • 双中括号支持更多的条件测试,例如 [[ $var == "string" ]][[ $var =~ pattern ]]
    • 双中括号在逻辑表达式中更自然,例如 [[ $var1 -eq 5 && $var2 == "string" ]]

双中括号 [[ ]] 更加强大、灵活且易于使用,它是在Bash中推荐使用的测试结构。但是,单中括号 [ ] 仍然在某些情况下有用,特别是在编写与POSIX兼容的脚本时或在其他Shell中运行时。

输出重定向

  • >:将标准输出重定向到指定文件中,覆盖原有内容。
  • >>:将标准输出重定向到指定文件中,追加到原有内容的末尾。
  • < :符号是输入重定向的一种形式。它从文件中读取输入并将其传递给命令
  • <<<:将一个字符串作为输入传递给命令。
  • &>>:将标准输出和标准错误输出都重定向到指定文件中,追加到原有内容的末尾。

echo

打印时保留换行

如果不使用引号,打印时会将输出视为单个字符串,换行符会被解释为空格或其他分隔符。所以,使用引号是确保保留换行的一种方法。

1
2
3
4
5
# 假设命令的输出包含了换行
output=$(command_to_generate_output)

# 打印变量时使用引号,以保留换行
echo "$output"

打印带有变量的消息

1
2
variable="Hello, Linux"
echo "变量的值是:$variable"

eval

eval 是一个内建的shell命令,它用来执行由参数组成的命令。在执行之前,会先对参数进行一次命令行展开,这意味着可以先对参数进行变量替换、命令替换等操作。

通常情况下,输入到shell的命令只会经过一次展开就直接执行了。但是使用 eval 可以使得命令行被二次展开,第一次展开生成新的命令行,然后这个新的命令行再次被展开并执行。

总结以上,将一个命令放入eval中,按下回车后,执行eval后的代码生成的结果,再对结果进行执行。

基本语法如下:

1
eval [arg ...]

awk

awk找到第二行第五列的值

1
df -h / | awk 'NR==2 {print $5}'

支持正则表达,Mem: 开头的行,打印第二列

1
free -m | awk '/^Mem:/{print $2}'

自定义分割,:分割(默认“ ”),打印以:分割的第二列

1
awk -F ':' '{print $2}'

动作块

awk 中的动作块可以执行多种操作,具体取决于您的需求和所处理的数据。以下是一些常见的 awk 动作块示例:

  1. 打印行:使用 {print} 打印整行。

    1
    awk '{print}' file.txt
  2. 打印特定字段:使用 {print $n} 打印第 n 个字段。

    1
    2
    # 打印最后一个是$FN
    awk '{print $2}' file.txt
  3. 计算和打印:执行数学计算并将结果打印。

    1
    awk '{sum += $1} END {print "Sum:", sum}' file.txt
  4. 条件打印:根据条件选择性地打印行。

    1
    awk '$3 > 50 {print}' file.txt
  5. 格式化输出:自定义输出格式。

    1
    awk '{printf "Name: %s, Age: %d\n", $1, $2}' file.txt
  6. 统计行数:统计行数。

    1
    awk 'END {print "Total lines:", NR}' file.txt
  7. 删除行:根据条件删除行。

    1
    awk '$4 != "error" {print}' file.txt
  8. 替换字段:替换字段的值并打印。

    1
    awk '{gsub("old", "new", $2); print}' file.txt
  9. 数组操作:使用数组存储数据并进行操作。

    1
    awk '{arr[$1] += $2} END {for (key in arr) print key, arr[key]}' fi

curl

** -k**

--insecure 选项。这会使 curl 忽略对服务器证书的验证,不检查证书的有效性

1
curl -k https://example.com

-w

取出响应参数,其中http_code,是响应编码;

-w 可填入的参数还有很多,毕竟响应的参数会有很多

1
curl -s -o /dev/null -w "%{http_code}" http://$ip_address

grep

-v

排除-v后的字符

1
grep -v process_name

比如:这个 grep process_name 命令本身也会出现在 ps 的输出中,因为它包含了要搜索的进程名称。这就创建了一个问题,即 grep 命令会匹配到包含字符串 “grep process_name” 的行,这并不是我们想要的。

-e

grep 命令可以使用 -e 选项来同时查询多个字符串。您可以将多个字符串用空格分隔,并将它们包含在引号内,以便一起传递给 -e 选项。以下是一个示例

1
grep -e "string1" -e "string2" -e "string3" file.txt

-A

打印匹配到的行+后5行内容

1
grep -A 5 '"tembin"'

-E 一行中同时匹配

1
grep -E 'pattern1.*pattern2' filename
  • pattern1pattern2 是你要匹配的条件。你可以根据需要替换它们。
  • .* 表示匹配两个模式之间的任意字符。这个符号可以用来指定两个模式之间的任意文本。
  • filename 是要搜索的文件名。

tr

去除指定字符

去除某些字符,比如在一个json文件中找到对应的IP,使用awk -F ':' '{print $2}’就会打印出IP,但最后一行带有"

1
tr -d '"'

替换

将前面的\n替换为空格

1
tr '\n' ' '

bc

bc 是一个用于进行任意精度数学计算的命令行计算器。它可以处理整数和浮点数,执行各种数学运算,包括加法、减法、乘法、除法、取余数、幂运算等。 bc 还支持数学函数、变量和条件语句,使它成为一个功能强大的数学工具。

lsmod

lsmod 命令用于列出已加载到Linux内核的模块(也称为内核模块)。内核模块是Linux内核的可插拔部分,它们可以在运行时加载和卸载,以提供额外的功能和驱动程序支持。

  1. 列出所有已加载的内核模块:

    1
    lsmod

    这个命令将列出系统中当前加载的所有内核模块,包括它们的名称、大小、使用次数和依赖关系。

  2. 查找特定内核模块:

    1
    lsmod | grep modulename

    这个命令可以用来查找特定名称的内核模块,其中 modulename 是您要查找的模块的名称。例如,如果要查找与KVM相关的模块,可以运行 lsmod | grep kvm

  3. 列出模块的详细信息:

    1
    modinfo modulename

这个命令用于获取特定内核模块的详细信息,包括作者、描述、版本等。将 modulename 替换为您要查询的模块的名称。

美化终端

需要以下Linux/Mac软件包:git、zsh

参考:程序员DJW(美化并增强Mac终端)

整体效果图

更改默认shell

切换默认shell为zsh:

1
2
chsh -s /bin/zsh	#切换到默认为zsh
echo $SHELL #查看默认shell

安装oh-my-zsh

1
2
3
4
5
6
# 下载oh-my-zsh
git clone https://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh

# 创建一个新的配置文件
cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc

安装插件

命令高亮插件

顾名思义就是打对命令会显示为绿色,命令不对显示为红色

1
2
3
4
5
# 下载命令高亮插件
git clone https://github.com/zsh-users/zsh-syntax-highlighting ~/.zsh/zsh-syntax-highlighting

# 配置插件生效
echo '# 命令高亮\nsource ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh' >> ~/.zshrc

命令提示插件

会将历史执行过的命令进行灰色显示,方便复用命令

1
2
3
4
5
6
# 下载命令提示插件
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions

# 配置插件生效
echo '# 命令提示\nsource ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh' >> ~/.zshrc

最后执行:

1
zsh

工作管理

工作管理即是,将前台应用放置于

程序放入后台(挂起)执行

  • 一行待执行命令结尾&,回车执行放入后台

    1
    2
    3
    4
    5
    #按照速度排序mirrorlist(比较耗时)
    Immortal-Arch# rankmirrors -n 6 /etc/pacman.d/mirrorlist.backup > /etc/pacman.d/mirrorlist &
    [1] 63059
    Immortal-Arch#
    [1] + done rankmirrors -n 6 /etc/pacman.d/mirrorlist.backup > /etc/pacman.d/mirrorlist
    • 其中[1]工作号,63059进程号
    • [1] + done +号说明最近,加入的工作号为[1]命令已经执行完成
  • 执行过程中Ctrl + Z,使前台命令暂停放于后台

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Immortal-Arch# top
    top - 02:53:43 up 1 day, 2 min, 1 user, load averag
    Tasks: 324 total, 1 running, 323 sleeping, 0 stop
    %Cpu(s): 0.9 us, 0.5 sy, 0.0 ni, 98.4 id, 0.0 wa,
    MiB Mem : 7801.9 total, 696.0 free, 3517.4 use
    MiB Swap: 0.0 total, 0.0 free, 0.0 use

    PID USER PR NI VIRT RES SHR S
    830 fuding 20 0 3026600 164788 105132 S
    594 root 20 0 508780 72880 38992 S

    [1] + 63720 suspended (signal) top524 8736 S #按下Ctrl + Z
    • 无论是暂停/后台运行都会往工作号+1

查看后台放入的命令

jobs命令可查看当前工作区暂停或后台运行的程序。

选项 含义
-l(L 的小写) 列出进程的 PID 号。
-n 只列出上次发出通知后改变了状态的进程。
-p 只列出进程的 PID 号。
-r 只列出运行中的进程。
-s 只列出已停止的进程。
1
2
3
4
5
6
7
8
9
10
849 ◯  jobs 
[1] suspended (signal) top
[2] - suspended (signal) top
[3] + suspended (signal) top
Immortal-Arch ॐ ~:
850 ◯ jobs -l
[1] 63720 suspended (signal) top
[2] - 64078 suspended (signal) top
[3] + 64112 suspended (signal) top

  • +号代表默认工作,恢复后台就是此进程
  • -号代表第二个加入的工作

把后台进程放到前台运行

查看当前后台程序

1
2
3
4
5
849 ◯  jobs 
[1] suspended (signal) top
[2] - suspended (signal) top
[3] + suspended (signal) top

fg命令恢复某个程序到前台

1
2
3
Immortal-Arch ॐ  ~:
856 ◯ fg %1 #执行后,按下Ctrl + c 结束运行

再次查看后台程序

1
2
3
4
5
Immortal-Arch ॐ  ~:
856 ◯ jobs
[2] - suspended (signal) top
[3] + suspended (signal) top

把暂停的工作恢复到后台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Immortal-Arch ॐ  ~:
863 ◯ jobs ⏎
[1] + suspended (signal) top
[3] - suspended (signal) top
Immortal-Arch ॐ ~:
863 ◯ top &
Immortal-Arch ॐ ~:
863 ◯ jobs #按下Ctrl + z ⏎
[1] - suspended (signal) top
[2] + suspended (signal) top
[3] suspended (signal) top
865 ◯ bg %2 ⏎
[2] 65915 continued top
[2] + 65915 suspended (signal) top

后台命令脱离终端运行

系统更新时如果退出终端或者退出登录就会使更新系统程序中断,这时就需要脱离终端运行且关闭终端继续运行。中断原因是,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

参考:

CSDN-koberonaldo24:linux后台运行、挂起、恢复进程相关命令

nohub

nohup的用途就是让提交的命令忽略 hangup 信号。nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出(终端不打印)和标准错误缺省会被重定向到 nohup.out 文件中(未实验),nohub就相当于一个保护进程,加上&才可以将进程放入后台运行(ps -ef可见),如果不使用onhub只在需要运行于后台的命令结尾加上&,就会使命令的打印在当前终端打印输出,并且关闭终端会使此命令中止运行,但不会影响新敲入的命令的执行。

nohub命令的使用:

1
2
3
4
5
6
7
8
9
nohup rankmirrors -n 6 ./mirrorlist.backup > ./mirrorlist &
[1] 69294 # 建立了进程的PID
nohup: ignoring input and redirecting stderr to stdout
jobs
[1] + running nohup rankmirrors -n 6 ./mirrorlist.backup > ./mirrorlist # running 说明在运行中
Immortal-Arch ॐ ~:
895 ◯
[1] + 69294 done nohup rankmirrors -n 6 ./mirrorlist.backup > ./mirrorlist # 运行完成状态 done ,并打印完成打印

  1. nohup图形化命令窗口点击关闭不会停止当前工作台的所有后台运行进程。
  2. nohup远程登录直接可以退出xshell等远程连接软件,后台程序不会退出
  3. 若要关闭,可使用ps -ef | grep <命令关键词>,搜索到PID,使用kill杀掉此后台进程。

ChatGPT

问题:

如果我在KDE图形化的命令行窗口执行了一个命令,此命令一直处于运行状态,如果此时关闭图形化的命令行窗口,这个一直处于运行状态的程序会受到HUP信号吗

ChatGPT:

如果您在KDE图形化的命令行窗口中运行的程序没有使用nohup等工具将其与终端分离,那么当您关闭终端窗口时,该程序将收到HUP信号并被终止。这是因为当您关闭终端窗口时,终端会将HUP信号发送给所有在此终端中运行的进程。

如果您希望运行的程序在终端关闭后继续运行而不受HUP信号的影响,可以在执行命令时使用nohup命令或将其与screen等工具一起使用,这将使程序与终端分离,并且不会受到HUP信号的影响。

setsid

如果我们换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点。 setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid 即可。

setsid命令和nohup使用方法一样,直接执行例如以下

1
setsid ping www.ibm.com

nohup和setsid都可以让进程脱离终端、不受HUP信号的影响,并在后台独立运行。它们的主要区别在于:

  1. 使用方式:nohup是在命令前加上nohup命令,例如:nohup command &;而setsid是通过系统调用setsid来创建一个新的会话和进程组。
  2. 进程控制:nohup命令不会创建新的进程组,因此进程仍然受到原来的进程组的控制,如果原来的进程组终止,该进程也会终止;而setsid创建一个新的进程组,进程不再受到原来的进程组的控制,即使原来的进程组终止,该进程也不会终止。
  3. 输出重定向:nohup命令会将标准输出和标准错误输出重定向到指定的文件中;而setsid不会自动将输出重定向,需要手动进行输出重定向。

因此,如果您需要在后台运行一个进程,并且希望该进程不受HUP信号的影响,可以使用nohup或setsid。如果您需要控制进程组并将输出重定向到文件中,可以使用nohup;如果您需要在新的会话和进程组中运行进程,并手动进行输出重定向,可以使用setsid。

管道符

1
2
3
cd aaa && ls        #前面的执行成功后面的才可以执行
cd aaa || ls #前面的执行不成功,后面的才执行,f反之后不执行

示例

软著

代码文件

申请软著需要一份.doc文档,将项目中所有文件(指定文件类型),正则匹配满足条件的文件类型去除空行输出到output.txt

1
find . -type f \( -name "*.vue" -o -name "*.js" \)  -exec sed '/^\s*$/d' {} + >> output.txt
  • find .找出所有本目录包含子目录-type f中的所有文件
  • {} +将查找到的所有文件放到{}处,+ 表示将多个文件一次传递给 sed 命令以提高效率。
  • -exec执行指令sed '/^\s*$/d'去除{}中文件所有空格
  • >>将所有内容追加到output.txt文件

代码总行

统计所有代码行数

1
wc `find . -name '*.js' -o -name '*.vue' -o -name '*.json'`