bash 命令
sed(待补充完整)
sed 命令是用来处理文本行的
sed
is a stream editor that works on piped input or files of text
以例子说明其用法
文件内容如下:a.py
Copy def fizz_buzz(limit):
for i in range(1, limit+1):
if i % 3 == 0:
print('fizz', end="")
if i % 5 == 0:
print('fizz', end="")
if i % 3 and i % 5:
print(i)
def main():
fizz_buzz(10)
if __name__ == "__main__":
main()
例子 1:使用正则表达式做文本替换
此处的 s/to/do/
表示将 to
替换为 do
,s
表示 substitute(替换)。
Copy $ echo howto | sed "s/to/do/"
howdo
例子 2:挑选指定行
此处表示挑选第 4 至第 5 行打印,在默认情况下 sed 命令会将每行都打印出来,使用 -n
选项可以抑制这一行为,4,5p
中的 p
表示 print(打印)。
Copy $ sed -n "4,5p" a.py
print ( 'fizz' , end= "" )
if i % 5 == 0:
可以使用 -e
选项选择多个行,注意到第 2 行与第 3 行打印了两次
Copy $ sed -n -e "1,4p" -e "2,3p" a.py
def fizz_buzz ( limit ) :
for i in range ( 1, limit+1 ) :
for i in range ( 1, limit+1 ) :
if i % 3 == 0:
if i % 3 == 0:
print ( 'fizz' , end= "" )
可以使用 5~3p
这种写法表示从第 5 行开始每隔 3 行进行打印
Copy $ sed -n -e '5~3p' a.py
if i % 5 == 0:
print ( i )
if __name__ == "__main__" :
例子 3:文本替换
只替换第一个匹配
Copy $ sed -n 's/=/!/p' a.py
if i % 3 != 0:
print ( 'fizz' , end! "" )
if i % 5 != 0:
print ( 'fizz' , end! "" )
if __name__ != "__main__" :
替换所有匹配
Copy $ sed -n 's/=/!/gp' a.py
if i % 3 !! 0:
print ( 'fizz' , end! "" )
if i % 5 !! 0:
print ( 'fizz' , end! "" )
if __name__ !! "__main__" :
另外也可以进一步将 gp
换成 gip
,其中 i
表示忽略大小写(case insensitive),另外 gip
三个字母的顺序可以随意调换
取前四行,将连续多个空格替换为一个空格
Copy $ sed -n '1,4s/ */ /gp' a.py
def fizz_buzz ( limit ) :
for i in range ( 1, limit+1 ) :
if i % 3 == 0:
print ( 'fizz' , end= "" )
多个替换规则,两种写法均可,注意到第 2 行没有任何模式被匹配上,由于 -n
参数的原因没有被打印出来。
Copy $ sed -n -e '1,4s/zz/aa/gp' -e '1,4s/==/!=/gp' a.py
$ sed -n '1,4s/zz/aa/gp;1,4s/==/!=/gp' a.py
def fiaa_buaa ( limit ) :
if i % 3 != 0:
print ( 'fiaa' , end= "" )
dirname
Copy $ dirname < file | dir > # 返回文件或目录的父目录
tee(待补充)
Copy $ tee a.txt b.txt # 同时向 a.txt 和 b.txt 中写入相同的内容,输入这行命令后,需要继续输入要写入的内容,以 Ctrl+Z 结束。注意 tee 命令写入的东西同时也会打印至屏幕上。
一般使用管道的方式进行使用,例如
Copy $ echo "abc" | tee a.txt b.txt
abc # 写入文件并同时将写入的信息输出至标准输出流
$ echo "abc" | tee a.txt > b.txt # 用重定向的方式同时写入两个文件,并且不显示在屏幕上
一个常见的错误参考例 7:修改屏幕亮度
printf
Copy $ printf "%-5s %-10s %-4.2f\n" 123 acb 1.23
printf 命令仿照 C 语言的 printf 函数,用于格式化输出,格式控制符 %-5s
表示左对齐(不加 -
则表示右对齐),最少 使用 5 个字符长度,以字符串的形式输出。格式控制符 %-4.2f
表示最少使用 4 个字符长度,小数点后保留 2 位,以浮点数形式输出。
!
在 shell 中,! 被称为 Event Designators ,用于方便地引用历史命令。
!20
表示获取 history 命令中的第 20 条指令;
!-2
表示获取 history 命令中的倒数第 2 条指令;
!echo
表示最近地一条以 echo
开头的指令;
!?data
表示最近的一条包含 data
的指令
echo、stty
echo 命令可以使用 -e
参数使得输出字符串中的转义字符产生效果;另外,echo 命令还可以控制输出的字体颜色,详细情形不赘述。
Copy $ echo -e "1\t2\t"
1 2
$ echo -e "\e[1;31mred\e[0m" # 输出的字体颜色为红色
Copy #!/bin/bash
# filename: password.sh
echo -e "Enter password"
stty -echo
read password
stty echo
echo Password read
echo "password is $password"
alias
别名相当于自定义命令,可以使用 alias 命令实现,也可以定义函数实现。此处仅介绍 alias 命令。
Copy $ alias myrm='rm -rf'
$ myrm data/
alias 命令产生的别名只在当前 shell 有效
du 与 df(待补充)
linux 中,目录本身占用一个 block,其大小为 4K,用于存储一些元数据,例如权限信息、修改时间等。
du 命令意为 disk usage,用于显示目录或文件的大小
Copy # 最基础的用法: -h意为--human-readable,表示自适应地使用K/M/G来显示文件(夹)大小
# 递归地显示<dir>以及所有子目录的大小(不会显示目录下的单个文件大小)
# 目录大小 = 目录大小(4kB)+当前目录下文件大小总和+子目录大小总和
$ du -h < di r >
# 显示<file>文件大小
$ du -h < fil e >
Copy $ du -sh < di r > # 只显示<dir>目录的大小
$ du -h --max-depth=1 < di r > # 只显示<dir>目录以及一级子目录大小
$ # 注意-s与--max-depth不能混用, --max-depth=0与-s的作用一致
$ du -h --max-depth=1 --exclude= < subdir > < di r > # 输出时不显示<subdir>的大小
$ du -Sh --max-depth=1 < di r > # 计算目录大小时不包括子目录大小
df 命令意为 disk free
watch
Copy # 每秒钟执行一次nvidia-smi命令以监控GPU使用情况,按ctrl+c退出监控
$ watch -n 1 nvidia-smi
sort
Copy $ docker images | sort -n -k 7
sort 命令的作用是以行为单位进行排序。-n
选项表示把字符当作数字进行排序,-k 7
选项表示选择第 7 列进行排序。可以使用 -t :
来指定列的分割符为 :
,可以使用 -r
选项进行降序排列(默认是升序排列)
Copy $ cat a.txt | sort -t ":" -k 2nr -k 3
先按第二列降序按数值大小排列, 再按第三列排序。
更多玩法可参照博客
paste
Copy paste -d= a.txt b.txt > c.txt
将 a.txt
与 b.txt
按行进行拼接,拼接字符为 =
,即:
Copy paste -sd+ a.txt | bc
-s
表示把 a.txt
的每行进行拼接,拼接字符为 +
,此条命令用于对一列数字求和
history(待补充)
history 命令用于显示历史命令。经过测试发现它的行为与 ~/.bash_history
文件有关,一个可以解释的逻辑如下,系统保留有一份历史命令的记录文件,进入一个终端后,执行任意一条指令都会在 .bash_history
文件末尾追加一条记录(不同终端有着独立的记录,互不影响),退出终端时,
打开两个终端,分别输入(各条命令的实际):
Copy $ echo dosome1
$ echo dosome3
Copy $ echo dosome2
$ echo dosome4
mount/umount
Copy $ mount -t nfs < devic e > < di r > # 将设备/目录device挂载到目录dir(称为挂载点)上, -t参数表示指定档案系统型态, 通常无需指定
$ umount < device/di r > # 取消挂载, 用挂载点或者设备名均可
若 umount 时出现 device is busy
的错误,可以参照链接 进行解决,摘录如下:
Copy $ # fuser命令使用 apt install psmisc 进行安装
$ fuser -m -v < di r > # 显示所有占用该目录的进程
$ fuser -k < di r > # 杀死所有占用该的进程
$ umount < di r >
$ # 以下为手动删除的办法, 但有时不奏效
$ # ps aux | grep <pid> # 显示该进程的信息
$ # kill -9 <pid> # 可以直接杀死进程
ps(待补充)
用于列出所有进程
参考链接
nmap
打印本机打开的所有端口信息
Copy $ # apt install nmap
$ nmap 127.0.0.1
ln
Copy $ # ln -s 原始路径 目标路径
$ ln -s /home/to/directory /data # 得到/data/directory
file -i
Copy $ file -i 文件名 # 查看编码格式
command
command 用于 shell 脚本中,忽略脚本定义的函数,而直接去寻找同名的命令
Copy function ls () {
echo "haha"
}
command ls # 输出: 当前路径下的文件(夹)
# 使用PATH环境搜索ls
command -p ls # 输出: 当前路径下的文件(夹)
另一种情况更加常见:在 ~/.bashrc
中为 ls
定义了 alias
Copy alias ls = 'ls --color=auto' # 自动按文件类型及权限显示目录内容
这种情况下
Copy command -p ls # 输出不带颜色
command -V ls # 输出: ls is aliased to `ls --color=auto`
command -v ls # 输出: alias ls='ls --color=auto'
getopt
stackoverflow
rsync
参考阮一峰博客
Copy rsync -anv source/ destination # 测试, 不实际执行
rsync -av source/ username@remote_host:destination
rsync -av --delete source/ destination # 确保两个目录完全一致
注:
此处 source
后面的斜杠如果省略, 则目标位置将会形成 destination/source
的目录结构, 这可能不是所期望的
awk
示例1
注意: 如果原始数据中以作为分割符, 但某些列可能为空字符串, 则必须指定
Copy awk - F ' \t ' '{print $1"\t"$2}' data . csv
示例2: 引入外部变量
Copy var = "123"
awk - v v = $ { var } '{print var" "$0}' data . csv
示例3: if
Copy awk '{if($2==23) {print $0}}' data . csv
free
Copy $ free -h
total used free shared buff/cache available
Mem: 3.8Gi 3.0Gi 300Mi 1.0Mi 523Mi 577Mi
Swap: 1.0Gi 585Mi 438Mi
在 Mem 这一行: total = used + free + buff/cache
, buff/cache
是指可以被操作系统回收的内存, 而 free
是完全没有被占用的内存, 而 used
里面包含 shared
, shared
主要是内核空间或共享库占的内存, 而 available
是估算新创建一个进程时可以使用的内存(注意是估算的, 不是精确值), 一般大于 free
, 但小于 free+buff/cache
.
在 Swap 这一行: total = used + free
操作系统能使用的内存可以认为是 Mem(total) + Swap(total)
, 而 Mem
一般是对应真正的内存, 而 Swap
一般是对应磁盘, 一般情况下, 操作系统为了计算机整体性能, 可能会将 Mem 与 Swap 互相交换 (例如把程序 A 的某部分内存从 Mem 暂时换到 Swap 上, 后续又换回来). 而有些编程语言允许将内存声明为 page-locked memory (也称为 pinned memory), 这样会避免操作系统将这部分内存放到 Swap 上, 从而保障这些内存的访问速度. 但注意, 显式声明 pinned memory 可能影响计算机整体性能, 应按需使用 (此处的所谓 page-locked memory 也就是 pytorch dataloader 里的那个参数 pin_memory=True
).
nc: 测试服务端口是否已经可用于接受连接
Copy $ until nc -z localhost 5000 ; do sleep 0.1 ; done
上述 nc
命令用于测试 localhost:5000
是否可以接受连接, until ...; do ...; done
是一个循环.
一般用于本地启动一个服务端程序绑定在 localhost:5000
, 然后使用上述命令测试服务是否成功启动.
xargs
xargs 用于将标准输入转换为参数
Copy # 等价于一条命令: `rm file1.txt file2.txt file3.txt` 默认使用空白符作为标准输入的分隔符
$ echo "file1.txt file2.txt file3.txt" | xargs rm
# 每次至多传递 2 个参数, 等价于 2 条命令:
# rm file1.txt file2.txt
# rm file3.txt
$ echo "file1.txt file2.txt file3.txt" | xargs -n 2 rm
# 指定分隔符
$ echo "file1.txt,file2.txt,file3.txt" | xargs -d ',' rm
# find -print0 会将列出的文件以 null 作为结尾, 而 -0 表示以 null 作为分隔符
$ find . -name "*.txt" -print0 | xargs -0 rm
# 使用 -I 参数等价于 2 条命令:
# mv file1.txt /backup/
# mv file2.txt /backup/
$ echo "file1.txt file2.txt" | xargs -I {} mv {} /backup/
Shell命令组合例子
例 2:管道、curl、grep、cut 结合使用
Copy $ curl --head --silent baidu.com | grep -i content-length | cut --delimiter= ' ' -f2
81
curl 命令用来请求 Web 服务器。其名字的含义即为客户端(client)的 URL 工具。具体用法可以参照阮一峰博客
它的功能非常强大,命令行参数多达几十种。如果熟练的话,完全可以取代 Postman 这一类的图形界面工具。——阮一峰博客《curl 的用法指南》
grep 的 -i
参数表示匹配时忽略大小写
例 3:source、export
通常情况下,执行如下语句
实际发生的事情是:创建一个子进程,在子进程中运行 a.sh
,然后回到当前 shell 中。注意子进程是用 fork 的方式产生的,因此子进程的环境变量与当前的 shell 是完全一致的。因此 a.sh
中设置的环境变量不会影响到当前的 shell。例子如下:
Copy # a.sh的内容
export FFFF = ffff
echo $ABCD
echo $FFFF
Copy $ export ABCD=abcd
$ export -p | grep ABCD
declare -x ABCD = "abcd"
$ ./a.sh
abcd
ffff
$ echo $FFFF # 没有输出
source 的作用是在当前 shell 中运行脚本内容, 使得脚本中设置的环境变量会影响到当前的 shell。例如:
Copy $ source a.sh
# 也等价于
$ . a.sh
备注:对于 source a.sh
这种执行方式来说,脚本中的 $0
为终端本身,例如:/bin/bash
,而 ./a.sh
这种执行方式,脚本中的 $0
将会是 ./a.sh
例 4:带颜色的终端输出
Copy echo -e "\e[44;37;5mabcs\e[0m"
\e[<...>m
表示 <...>
中的部分为设置输出格式(字体颜色,背景颜色,是否加粗,是否产生闪烁效果等),\e
也可以用 \033
代替。
44;37;5
:44
表示设置背景色为蓝色,37
表示设置前景色(也就是字体颜色)为白色,5
表示字体产生闪烁效果 。
备注:这些数字实际上被称为 SGR 参数(Select Graphic Rendition) ,这些数字的顺序是不重要的,完整的列表可以参见 ANSI escape code - Wikipedia ,简短版的说明可以参见 简书 。
例 5:排除某些子目录的复制
Copy $ ls ./src | grep -v 'logs\|images' | xargs -i cp -r ./src/{} ./dst
排除 logs
与 images
子目录从 src
复制文件至 dst
Copy ROOT
- src
- logs/
- images/
- models/
- main.py
- dst
grep -v
表示排除,'logs\|images'
表示或的关系
xargs -i
表示将前一步的结果放在 ./src/{}
的 {}
处。
例 6:grep、xargs
Copy find . -name "*.py" | xargs grep -n "Model"
查找当前目录及子目录下所有 .py
文件含有关键词 Model
的行及行号。
例 7:修改屏幕亮度
Copy $ echo 5000 | sudo tee /sys/class/backlight/intel_backlight/brightness
# 不能使用 sudo echo 5000 > /sys/class/backlight/intel_backlight/brightness
例 8:依据 csv 格式文件执行命令(read 命令)
cpfiles.txt
文件如下,希望按照 csv 进行文件拷贝
Copy a.txt,a1.txt
b.txt,b1.txt
Copy $ cat cpfiles.txt | while IFS = "," read src dst ; do cp $src $dst; done
$ cat cpfiles.txt | while IFS = "," read -a row ; do cp ${row[0]} ${row[1]}; done
例 9:生成序列(seq 命令)
Copy $ for i in $( seq 100000 ); do echo ${i} >> x.txt ; done
例 10:打印 Git Objects
Copy $ find .git/objects/ -type f | awk -F "/" '{print $3$4}' | while read -r obj ; do echo ========= ; echo ${obj} $( git cat-file -t ${obj}); echo $( git cat-file -p ${obj}); done ;
输出
Copy =========
08585692ce06452da6f82ae66b90d98b55536fca tree
100644 blob 78981922613b2afb6025042ff6bd878ac1994e85 a.txt
=========
4b825dc642cb6eb9a060e54bf8d69288fbee4904 tree
=========
78981922613b2afb6025042ff6bd878ac1994e85 blob
a
=========
db28edd91114108e88d430f317c46f87e9cb2896 commit
tree 08585692ce06452da6f82ae66b90d98b55536fca author xxx <xxx@qq.com> 1663866229 +0800 committer xxx <xxx@qq.com> 1663866229 +0800 add a.txt
例 11: 跳过前 k 行
Copy # tail -n +<N+1> <filename>
tail -n +3 a.txt # 跳过前2行
例 12: 列出当前目录下各个文件或文件夹大小
Copy find . -maxdepth 1 -print0 | xargs -0 -i du -sh {}
Shell 脚本示例
例 1:定时计数
Copy #!/bin/bash
echo -n Count:
tput sc # 保存当前光标位置
count = 0
while true ; do
if [ $count -lt 15 ]; then
let count++
sleep 1
tput rc # 将光标返回到上一个存储位置
tput ed # 清空当前光标到结尾的所有字符
echo -n $count;
else exit 0 ;
fi
done
例 2:输入密码
Copy #!/bin/bash
echo -e "Enter password"
stty -echo # 抑制输出
read password
stty echo # 显示输出
echo Password read
echo "password is $password"
例 3:移动文件
Copy for subdir in $( ls ./train );
do
for filename in $( ls ./train/ ${subdir});
do
mv ./train/ ${subdir} / ${filename} ./temp/ ${filename};
done
done
例 4:修改文件后缀
Copy for file in $1 /*.dat ;
do
mv "$file" "${file % . * }.txt" ;
done