IP+子网掩码可以在局域网内使用;
IP+子网掩码+网关+DNS服务器缺一不可才能访问互联网
标题Linux配置IP地址永久生效的方法:
shell 指令
当可视化界面无法完成一些定制的功能时,shell 说不定可以。毕竟使用 UI 的功能也是开发 UI 的人设计的,不可能面面俱到。
shell 的文本工具缺可以相互组合,用不同的组合或者编程方式把一些 GUI 无法完成的工作完成。
windows 的 powershell ,linux sehll,形式、语法上可能不一样,但是思想是一样的。最常见的 shell bash,以此来学习。
打开 shell,一个命令提示符,这些全都可以自己配置。和计算机交互的文本界面。
基本的使用方式,输入命令,当个命令加参数是最简单的使用方式。date
,执行一个名为 date 的程序。
m@vm:~$ date
2024年 08月 22日 星期四 12:20:37 CST
m@vm:~$
然后,shell 等待我们输入其他命令。可以在执行命令的同时向程序传递 参数 :
m@vm:~$ echo hello
hello
m@vm:~$
执行一个名为 echo 的程序,输出一点东西。参数由空格分开,如果要输入一个包含空格的参数
m@vm:~$ echo “hello world”
hello world
m@vm:~$ echo hello\ world
hello world
当输入一个程序名时,shell 怎么找到程序在哪里的?首先这些程序肯定在计算机里,
通过环境变量来实现,环境变量像是个编程语言里的变量保存着一些东西,bash 也确实是一个编程语言。可以 while 循环 for 循环 条件语句 。在 shell 中定义变量,定义函数。下一节(shell 脚本)
环境变量在启动shell就设置了,有一堆这些东西,如主目录,用户名,搜索路径的变量是路径变量
m@vm:~$ echo PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/m/baremetal/arm−gnu−toolchain−13.3.rel1−x8664−arm−none−eabi/binm@vm: PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/m/bare_metal/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin m@vm:~PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/m/baremetal/arm−gnu−toolchain−13.3.rel1−x8664−arm−none−eabi/binm@vm:
用:分割的目录,当输入程序名时,shell 按照上面的路径依次查找。直到找到为止。使用 which 可以查看使用的是哪个可执行文件。
m@vm:~$ which echo
/usr/bin/echo
m@vm:~$
也可以绕过 $PATH 直接通过绝对路径指定要执行的可执行文件。
m@vm:~$ /usr/bin/echo hello
hello
m@vm:~$
当前目录 pwd,cd 切换,. 和 … 的含义
注意,shell 会实时显示当前的路径信息。可以通过配置 shell 提示符来显示各种有用的信息。
运行一个程序时,如果我们没有指定路径,则该程序会在当前目录下执行。例如查看指定目录下包含哪些文件,我们使用 ls 命令默认列出当前的,也可以用参数指定
m@vm:/home$ ls /home/m
两个特殊的符号,~
扩展为用户文件夹,-
之前在的目录,一般 cd -
来快速切换
程序的参数或标志,ls --flag
ls -l 长列表,权限,chmod chown
文件的权限很好理解,目录的权限,
- 读取:是否被允许看到这个目录中的文件
- 写入:是否允许在目录中重命名、创建、删除文件
- 执行:搜索权限,是否允许进入该目录
如果对一个文件有写入权限,但没有对目录的写入权限,那么就不能删除文件,可以清空内容但不能删除,因为这要写入目录本身。
目录的执行权限,如果想打开一个文件,用 cd 进入一个目录,必须有该目录和父目录的执行权限。如要访问 /usr/bin/echo ,必须有 / usr bin 目录的执行权限。
还需要掌握的命令 mv cp mkdir rmdir rm
mv 用来移动和重命名。cp 命令也很类似,在复制的时候可以指定复制过去的名字。
rm 命令,在linux默认删除不递归,无法用rm移除一个文件,需要加上 -r 进行递归删除,即删除一个目录下所有的东西。rmdir 只能删除一个空目录,这是一种安全机制。mkdir 创建新目录,对于包含空格的目录,则需要引用或加\
在没有网络的时代,查手册,man 以另一个程序的名字作为参数。给出了非常详细的说明。
ctrl + L 清除中断,返回顶部。
程序之间的连接
前面都是单独一个程序运行的情况,当开始组合不同的程序时,shell 的强大功能才能体现出来。
终端与文件交互,并让文件在不同程序之间传输。shell 实现这个东西的基础概念:流
程序有两个主要的“流”:它们的输入流和输出流。当程序尝试读取信息时,它们会从输入流中进行读取,当程序打印信息时,它们会将信息输出到输出流中。通常,一个程序的输入输出流都是终端。也就是,键盘作为输入,显示器作为输出。shell 提供了重定向的方法,改变程序输入输出的指向。
最简单的重定向是 < file 和 > file。这两个命令可以将程序的输入输出流分别重定向到文件:
m@vm:~/missing$ echo hello > hello.txt
没有输出,输出被定向到了 Hello.txt 中。用 cat 可以查看。
cat 可以将输入与输出连接起来。比如一个例子
m@vm:~/missing$ cat < hello.txt > hello2.txt
不使用 cp 命令的复制。关键在在于,cat 并不知道自己在干什么,不知道重定向,也不知道什么是复制,只是按照规定的方式运行。但是让 shell 使用 hello.txt 作为 cat 的输出,并将 cat 打印的任何输出写入 hello2.txt,实现了和 cp hello.txt hello2.txt 一样的功能。这部分是 shell 设计的核心理念,用简单的工具组合来做更复杂的事。
>>
表示追加,
这些也只是简单的操作。shell 提供另一个运算符,管道 | ,左边的程序作为输出,右边的程序作为输入。
比如只看根目录的最后一行 ls -l / | tail
m@vm:~/missing$ ls -l / | tail --lines=1
m@vm:~/missing$ ls -l / | tail -n1
ls 不知道 tail 的存在,tail 也不知道有 ls,这是两个不同的程序,也没再设计时做兼容性的适配,他们只是从输入读了一些东西,然后按照自己的处理方式处理后输出。管道连接二者,,希望ls的输出作为tail的输入。
可以做一些更有趣的事情,数据处理。
m@vm:~/missing$ curl --head --silent baidu.com | grep --ignore-case content-length | cut --delimiter=’ ’ -f2
81
这个命令的作用是从 baidu.com 的 HTTP 响应头中提取 Content-Length 头的值。或者从 ifconfig 的输出中提取 ip
ifconfig ens33 | grep ‘inet’ | head --lines=1 | cut --delimiter=’ ’ -f10
可以看到用各种工具可以实现有趣的事情。除了文本,管道还可以处理图像甚至是视屏。
后面数据处理更详细的学。
root 用户的讨论
对于大多数的类 Unix 系统,根用户(root user)是非常特殊的,在上面的输出结果中,根用户几乎不受任何限制,他可以创建、读取、更新和删除系统中的任何文件。 通常在我们并不会以根用户的身份直接登录系统,因为这样可能会因为某些错误的操作而破坏系统。 取而代之的是我们会在需要的时候使用 sudo 命令。
向 sysfs 文件写入内容必须根用户才能做。sysfs 挂在到 /sys 下,里面有各种内核参数,不需要任何专业工具,可以直接访问操作系统内核的东西。
在 /sys/class/leds/input1::capslock 下有个 brightness 文件夹,可以直接写入参数控制 led 亮度。
如果尝试 echo 1 > brightness
没有权限,会被拒绝,修改内核是不被允许的。如果 sudo echo 1 > brightness
也是不被允许的。问题出在了管道的设计上。
输入输出重定向程序并不知道。| <> 都是通过shell 执行的,上面的代码为了能让 sudo echo 的输出写入 brightness,会先打开 brightness(这个设计也很好理解),此时还是普通用户。
解决方法,直接整个切换,su 换到root,提示符也变成了 #,然后上面的就可以执行了。
基于对 shell 工作方式的理解,还可以有一种方法
m@vm:/sys/class/leds/input1::capslock$ echo 1 | sudo tee brightness
tee 用来读取标准输入的内容,然后输出到一个文件和标准输出。标准输入一分二。
让 tee 以 root 用户执行,输出到 brightness,这个事情是可以的。
如果写一个程序,在收到邮件时,让键盘上的一个灯亮起来,这也是个很好玩的事情。
xdg-open
touch
shell 工具和脚本
变量语法,流程控制,函数
shell 脚本
很多情况下我们需要执行一系列的操作并使用条件或循环这样的控制流。shell 脚本的复杂性进一步提高。
shell 脚本与其他脚本语言不同之处在于,shell 脚本针对 shell 所从事的相关工作进行了优化。因此,创建命令流程(pipelines)、将结果保存到文件、从标准输入中读取输入,这些都是 shell 脚本中的原生操作,这让它比通用的脚本语言更易用。
定义变量作为学习语言的第一步。
在 bash 中为变量赋值的语法是 foo=bar
,访问变量中存储的数值,其语法为 $foo
。 需要注意的是,foo = bar
无法按照预期工作的,解释器会调用程序 foo
并将 =
和 bar
作为参数。在 shell 脚本中使用空格会起到分割参数的作用。
关于字符串的使用。’ 里的所有内容会直接输出。" 定义的字符串里的变量会替换
m@vm:~/missing$ echo "foo"barm@vm: /missingfoo"
bar
m@vm:~/missingfoo"barm@vm: /missing echo ‘$foo’
$foo
bash 也支持 if, case, while 和 for 这些控制流关键字。同样地, bash 也支持函数,它可以接受参数并基于参数进行操作。
写一个脚本名为 mcd.sh
mcd () {
mkdir -p "$1"
cd "$1"
}
在 shell 里执行 source mcd.sh
看起来没什么事情发生,但是 shell 中已经有 mcd() 函数了。在终端执行 mcd test
就可以看到变化了。
bash 里的特殊变量
$0
脚本名$1
-$9
脚本的参数。$1
是第一个参数,依此类推。$@
所有参数$#
参数个数$?
前一个命令的返回值$$
当前脚本的进程识别码!!
完整的上一条命令,包括参数。常见应用:当因为权限不足执行命令失败时,可以使用 sudo !! 再尝试一次。$_
上一条命令的最后一个参数。
命令通常使用 STDOUT 来返回输出值,使用 STDERR 来返回错误及错误码,便于脚本以更加友好的方式报告错误。 返回码或退出状态是脚本/命令之间交流执行状态的方式。返回值 0 表示正常执行,其他所有非 0 的返回值都表示有错误发生
通过 $? 来查看,退出码可以搭配 &&(与操作符)和 ||(或操作符)使用,用来进行条件判断,决定是否执行其他程序。它们都属于短路 运算符(short-circuiting) 同一行的多个命令可以用 ; 分隔。程序 true 的返回码永远是 0,false 的返回码永远是 1。几个例子
m@vm:~/missing/test$ false || echo “hello”
hello
m@vm:~/missing/test$ true || echo “hello”
m@vm:~/missing/test$ true && echo “hello”
hello
m@vm:~/missing/test$ false && echo “hello”
m@vm:~/missing/test$ false ; echo “hello”
hello
m@vm:~/missing/test$
用或运算连接两个命令,如果第一个失败了,就执行第二个。如果第一个成功了,就不执行了。
用与连接两个命令,如果第一个成功了,执行第二个,如果第一个失败了,就不执行第二个了。
此外 ; 可以分割两个命令
将一个命令的输出保存到一个变量中 foo=$(pwd)
,通过 命令替换(command substitution)实现。两个命令嵌套。
通过 $( CMD ) 这样的方式来执行 CMD 这个命令时,它的输出结果会替换掉 $( CMD ) 。例如,如果执行 for file in $(ls) ,shell 首先将调用 ls ,然后遍历得到的这些返回值。
还有李哥小众工具 进程替换(process substitution), <( CMD )
会执行 CMD 并将结果输出到一个临时文件中,并将 <( CMD ) 替换成临时文件名。这在我们希望返回值通过文件而不是 STDIN 传递时很有用。例如, diff <(ls foo) <(ls bar)
会显示文件夹 foo 和 bar 中文件的区别。
通过看例子来学脚本怎么写
#!/bin/bash
echo "Starting program at $(date)" # date会被替换成日期和时间
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# 如果模式没有找到,则grep退出状态为 1
# 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
我们比较 $?
是否等于 0,。
bash 脚本里的通配符,? 匹配一个字符,*匹配任意字符。 此外 {}
还会自动展开
如 image.{jpg,png}
会自动展开为 image.jpg image.png
。
mv *{.py,.sh}
会移动所有的 py 和 sh 脚本。
脚本不一定用 bash 写才能在终端里调用,如果是 python 脚本,也可以
#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)
脚本开头的第一行是 shebang,总之就是 #! 后面跟着解释器的路径,增加脚本的可移植性。
还有一种方法 #!/usr/bin/env python
使用 env 命令,env 使用 $PATH
环境变量来定位。
shell 函数和脚本有如下一些不同点:
- 函数只能与 shell 使用相同的语言,脚本可以使用任意语言。因此在脚本中包含 shebang 是很重要的。
- 函数仅在定义时被加载,脚本会在每次被执行时加载。这让函数的加载比脚本略快一些,但每次修改函数定义,都要重新加载一次。
- 函数会在当前的 shell 环境中执行,脚本会在单独的进程中执行。因此,函数可以对环境变量进行更改,比如改变当前工作目录,脚本则不行。脚本需要使用 export 将环境变量导出,并将值传递给环境变量。
- 与其他程序语言一样,函数可以提高代码模块性、代码复用性并创建清晰性的结构。shell 脚本中往往也会包含它们自己的函数定义。
shell 工具
命令的使用方法
如何知道一个命令有哪些参数。最常用的是使用 -h
或 --help
。
更详细的是使用 man
命令。第三方的工具,如果开发者写了手册,也可以通过这个命令查得到。
很详细的手册,详细到阅读起来不友好。
更好的工具 tldr
,too long dont read,给出了最常用的参数。
当然问 chatgpt 的效率也很高。
一些常用的
查找文件 find
最基本的用法
find . -name *.c
find . -name '*.c'
查找文件夹
find . -type d -name build
-type
指定要查找的类型
查找所有文件夹路径中包含test的python文件
find . -path '*/test/*.py' -type f
一般也就够用了。但是也有一些更复杂的功能,了解一下,用到的时候查就好了。
可以只等文件夹的查找深度,文件的大小,甚至可以找到文件后执行操作。
- find . -name ‘*.png’ -exec convert {} {}.jpg ;
查找代码
有时候要找的是文件内容,使用 grep 工具
查找 shell 命令
找之前使用过的一套命令。使用 history 命令
history | grep find
快捷键 ctrl + R
使用 fzf 工具,把一些东西管道给 fzf,模糊查找。
目录与文件夹导航
使用 cd - 可以在两个文件夹之间来回切换。
vim 编辑器
写作和写代码其实是两项非常不同的活动。当我们编程的时候,会经常在文件间进行切换、阅读、浏览和修改代码,而不是连续编写一大段的文字。因此代码编辑器和文本编辑器是很不同的两种工具(例如微软的 Word 与 Visual Studio Code)。
当然 visual stuido code 是最流行的编辑器,vim 是最流行的基于命令行的编辑器。有的时候,没有vsc 不得不用 vim 来处理一些东西。
但是 vim 有一些学习成本。有一些设计的思想可以参考。
Vim 的哲学
在编程的时候,会把大量时间花在阅读/编辑而不是在写代码上。所以,Vim 是一个 多模式 编辑器:它对于插入文字和操纵文字有不同的模式。Vim 是可编程的(可以使用 Vimscript 或者像 Python 一样的其他程序语言),Vim 的接口本身也是一个程序语言:键入操作(以及其助记名) 是命令,这些命令也是可组合的。Vim 避免了使用鼠标,因为那样太慢了;Vim 甚至避免用上下左右键因为那样需要太多的手指移动。
这样的设计哲学使得 Vim 成为了一个能跟上你思维速度的编辑器。
vim 的设计想法是 Vim 的界面本身是一个程序语言。键入操作(以及他们的助记名) 本身是命令,这些命令可以组合使用。这使得移动和编辑更加高效,特别是一旦形成肌肉记忆。
shell 的 vim 模式
bash set -o vi
fish fish_vi_key_bindings
数据整理
当使用管道运算符的时候,其实就是在进行某种形式的数据整理。
例如这样一条命令 journalctl | grep -i intel,它会找到所有包含 intel(不区分大小写)的系统日志。您可能并不认为这是数据整理,但是它确实将某种形式的数据(全部系统日志)转换成了另外一种形式的数据(仅包含 intel 的日志)。大多数情况下,数据整理需要您能够明确哪些工具可以被用来达成特定数据整理的目的,并且明白如何组合使用这些工具。
日志处理是典型的需要做数据处理的场景。
比如查看哪些人什么时候登陆过服务器。
journalctl | grep sshd
journalctl | grep sshd | grep 'Disconnected' > ssg.log
还是有很多无关的数据,可以删除掉,使用 sed 工具
最常用的替换 sed 's/被替换的/替换内容/'
一些特殊的符号
.
除换行符之外的 “任意单个字符”*
匹配前面字符零次或多次+
匹配前面字符一次或多次[abc]
匹配 a, b 和 c 中的任意一个(RX1|RX2)
任何能够匹配 RX1 或 RX2 的结果^
行首$
行尾
journalctl | grep sshd | grep 'Disconnected' | sed 's/.*Disconnected from //'
匹配任何开头的内容和 Disconnected from ,然后替换成什么都没有。
这是 sed 常用的使用方法。还有更多的功能。
想做更复杂的事情,可以使用在线的正则表达式工具调试,或者直接询问 gpt。
为了完成某种匹配,最终可能会写出非常复杂的正则表达式。关于如何匹配电子邮箱地址一点也不简单。
sed 还有更多的功能,如文本注入,打印特定行。
sort 会对其输入数据进行排序。uniq -c 会把连续出现的行折叠为一行并使用出现次数作为前缀。我们希望按照出现次数排序,过滤出最常出现的用户名:
ssh myserver journalctl
| grep sshd
| grep "Disconnected from"
| sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
| sort | uniq -c
| sort -nk1,1 | tail -n10
| awk '{print $2}' | paste -sd,
awk 是另外一种编辑器。其实是一种编程语言。只看看最基本的用法就好。
awk 一个代码块。在代码块中,$0
表示整行的内容,$1
到 $n
为一行中的 n 个区域,区域的分割基于 awk 的域分隔符(默认是空格,可以通过 -F 来修改)。在这个例子中,我们的代码意思是:对于每一行文本,打印其第二个部分,也就是用户名。
此外还可以分析数据,统计数据,甚至画图
命令行环境
改善 shell 及其他工具的工作流的方法,提高效率,这主要是通过定义别名或基于配置文件对其进行配置来实现的。
任务控制
中断正在执行的任务,比如当一个命令需要执行很长时间才能完成时(假设我们在使用 find 搜索一个非常大的目录结构)。大多数情况下,我们可以使用 Ctrl-C 来停止命令的执行。
结束进程
shell 使用 unix 提供的信号机制来和进程通信。一个进程接收到信号时,会立即处理该信号。信号是一种软件中断。
Ctrl + C ,shell 会发送一个 SIGINT 到进程。
暂停和后台执行
SIGSTOP 会让进程暂停。在终端中,键入 Ctrl-Z 会让 shell 发送 SIGTSTP 信号。
可以使用 fg
或 bg
命令恢复暂停的工作。它们分别表示在前台继续或在后台继续。
jobs 命令会列出当前终端会话中尚未完成的全部任务。
命令中的 &
后缀可以让命令在直接在后台运行,这使得您可以直接在 shell 中继续做其他操作,不过它此时还是会使用 shell 的标准输出。
终端多路复用
tmux
别名
shell 的别名相当于一个长命令的缩写,bash 中的语法
alias alias_name="command_to_alias arg1 arg2"
=
两边没空格,alias 是一个命令。
# 创建常用命令的缩写
alias ll="ls -lh"
# 能够少输入很多
alias gs="git status"
alias gc="git commit"
alias v="vim"
# 手误打错命令也没关系
alias sl=ls
# 重新定义一些命令行的默认行为
alias mv="mv -i" # -i prompts before overwrite
alias mkdir="mkdir -p" # -p make parent dirs as needed
alias df="df -h" # -h prints human readable format
# 别名可以组合使用
alias la="ls -A"
alias lla="la -l"
# 在忽略某个别名
\ls
# 或者禁用别名
unalias la
# 获取别名的定义
alias ll
# 会打印 ll='ls -lh'
为了让自己的设置持续生效,可以吧配置放到 shell 的启动文件里。
配置文件
很多程序通过 dotfile 的配置文件来完成的(因为它们的文件名以 . 开头,例如 ~/.vimrc
。也正因为此,它们默认是隐藏文件,ls 并不会显示它们)。
shell 的配置也是通过这类文件完成的。在启动时, shell 程序会读取很多文件以加载其配置项。对于 bash 来说,可以通过编辑 .bashrc
或 .bash_profile
来进行配置。在文件中可以添加需要在启动时执行的命令,例如别名,或者是环境变量。
很多程序都要求在 shell 的配置文件中包含一行类似 export PATH="$PATH:/path/to/program/bin"
的命令,这样才能确保这些程序能够被 shell 找到。
一种好的管理配置文件的方式:将所有的配置文件放到一个文件夹内使用版本控制系统进行管理,用脚本将其符号链接到需要的地方。好处
- 新设备安装简单
- 可移植性,工具在任何地方都能以相同的配置工作
- 同步
- 修改追踪
配置文件的可移植性
配置文件的一个常见的痛点是它可能并不能在多种设备上生效。例如,操作系统或者 shell 是不同的,则配置文件是无法生效的。或者,有时仅希望特定的配置只在某些设备上生效。
有一些技巧可以轻松达成这些目的。
if [[ "$(uname)" == "Linux" ]]; then {do_something}; fi
# 使用和 shell 相关的配置时先检查当前 shell 类型
if [[ "$SHELL" == "zsh" ]]; then {do_something}; fi
# 也可以针对特定的设备进行配置
if [[ "$(hostname)" == "myServer" ]]; then {do_something}; fi
远程设备
通常的 ssh 命令 ssh user@ip
ssh 的一个经常被忽视的特性是它可以直接远程执行命令。 ssh foobar@server ls
可以直接在 foobar 执行 ls 命令。 想要配合管道来使用也可以, ssh foobar@server ls | grep PATTERN
会在本地查询远端 ls 的输出而 ls | ssh foobar@server grep PATTERN
会在远端对本地 ls 输出的结果进行查询。