Linux shell笔记
AndreGeng opened this issue · 1 comments
AndreGeng commented
Shell基础
- 查看进程
- ps -ef
- top
- 杀死进程 kill pid
- 查看磁盘空间
- df -h // 查linux服务器的文件系统的磁盘空间占用情况
- du -h // 查看当前目录下各文件大小
- 处理数据文件
- 排序
- sort -n // 按数字来排序
- sort -t ':' -k 3 -n /etc/passwd // 以':'为分隔符,选取第3列,按数字来排序
- du -sh | sort -nr
- 搜索之grep
- grep -v textneedtosearch file1 // 反向搜索
- grep -c textneedtosearch file1 // 只显示匹配的个数
- grep -e textneedtosearch -e textneedtosearch2 file1 // 用e指定多个模式
- grep [tf] file1 // 用正则进行搜索
// grep -r 递归查询文件夹,-i 不区分大小写, -c 只显示匹配到的个数 - grep -ric 'hahas' ./src | awk -F: '$2>0 {printf "%s:%s\n", $1,$2}'
- 搜索之ag
- ag -c hahas ./src | awk -F: '{printf "%s:%s\n", $1, $2}'
- 归档与压缩
- gzip
- tar
- tar -cvf test.tar test1.txt test2.txt
- tar -xvf test.tar
- tar -zxvf filename.tgz // 用于解压gzip压缩过的tar文件
- xargs
- xargs可以将输入内容(通常通过命令行管道传递),转成后续命令的参数
- 命令组合:尤其是一些命令不支持管道输入,比如ls. e.g. ls *.js | xargs ls -al
- ls *.js | xargs -t ls -al // -t 在执行后面的命令前,先将命令打印出来, 这样比较容易排查错误
- 参数替换: ls *.js | xargs -t -I '{}' mv {} {}.backup // 大写的i 可以指定用来替换的点位符
// xargs 默认以空格为分隔符,如果文件名包含有空格会导致解释失败, -print0:告诉find命令,在输出文件名之后,跟上NULL字符,而不是换行符. -0: 通知xargs以NULL做为分隔符。 - find . -name '*.css' -print0 | xargs -0 -t ls -al
子shell
- 在父shell中运行/bin/bash, 会生成一个子shell, 可以通过ps -f或者ps --forest观察到这种变化。
- 另外一种生成子shell的方式是'进程列表'
// 所有命令顺序执行,但这并不是进程列表
// ps: ';'与&&的差别,';'在前一个命令失败后还是会运行后面的命令,而&&则不会- pwd ; ls ; cd ~ ; pwd ; ls
// 下面才是进程列表的写法, 'echo $BASH_SUBSHELL'可以用来检测命令是否生成了子shell - (pwd ; ls ; cd ~ ; pwd ; ls; echo $BASH_SUBSHELL)
- 一种常见的用法是把进程列表放入后台执行。e.g. (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)&
- 协程(coproc)+ 进程列表
- pwd ; ls ; cd ~ ; pwd ; ls
shell内部命令与外部命令
- 外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin中
- 内建命令和外部命令的区别在于前者不需要使用子进程来执行. 可以用type来区分一个命令是否为内建命令
type cd // cd is a shell builtin
- 有些内建命令也有对应的外部命令
type -a echo // echo is a shell builtin; echo is /bin/echo
- 内建命令
- history用来展示shell中的命令历史记录
- 如果打开了多个shell窗口,它们之间的历史记录并不会同享的,可以使用history -n >> /dev/null 来加载命令行历史记录。
- !!可以取出最近执行的一个命令
- alias
- alias // 列出现在所有设置的别名
- history用来展示shell中的命令历史记录
Linux 环境变量
- 全局环境变量与局部环境变量
- 全局环境变量对于shell会话和所有生成的子shell都是可见的。局部变量则只对创建它们的 shell可见。
- 用env/printenv命令打印全局环境变量
- set命令会显示出全局变量、局部变量以 及用户定义变量
- 用户自定义变量
- 定义方法, 自定义变量请使用小写字母
my_variable=Hello
echo $my_variable
my_variable = "Hello World"
echo $my_variable
- 创建全局变量
my_variable=hello
export my_variable
- 删除全局变量
unset my_variable
- 定位系统环境变量
- 登录shell
- 交互式shell进程
- 非交互式shell进程
- 数组变量(可移植性不好,不怎么常用)
mytest=(one two three four five)
echo $mytest // one
echo ${mytest[2]} // three
echo ${mytest[*]} // one two three four five
mytest[2]=seven
echo ${mytest[*]} // one two seven four five
unset mytest
Linux账户
- /etc/passwd // 用户名 UID 登陆shell 关联文件
- 500以下的UID分配给系统, 用户的uid一般大于500
- /etc/shadow
- 账户相关操作
useradd USERNAME
userdel USERNAME
usermod
passwd USERNAME
- 组相关操作
- /etc/passwd
groupadd NEWGROUPNAME
groupmod
usermod -G GROUPNAME USERNAME // 把USERNAME添加天GROUPNAME的组中
- 文件权限
- umask
umask // 0022, umask里面存储的为掩码, 对文件来说,全权限的值是666(所有用户都有读
和写的权限);而对目录来说,则是777.
举例来说,对umask值为0022的系统, 创建文件时,默认权限为644。创建目录时,默认权限为755
ps: umask第一位的值为粘着位(sticky bit)
- 问题
+ 目录的执行权限是什么意思?
目录的执行权限指的是可“搜索”的含义,e.g.你要访问/etc/passwd文件,只拥有对目录/etc和文件passwd的读权限是不够的。还需要拥有目录/etc的可搜索/执行权限。
+ 粘着位是什么?
粘着位分为suid = 4, sgid = 2, 其它人的粘着位 = 1. 它的主要作用:
1. 给目录设置粘着位:每个用户都能在这个目录里面创建文件(前提是这个目录的权限为777),但是只能删除自己创建的文件。chmod u+t DIRECTORY, 755的文件夹权限会变为drwtr-tr-t
2. 给目录设置suid或者guid位,可以使文件/文件夹具有当前用户的权限,如果创建文件的用户为root, 并设置了suid, 即使以后运行文件的用户为普通用户,也会以root的权限来运行文件。
参见:粘着位解释- 更改权限
chmod 777 FILE
chmod u+x FILE // u: 用户,g: 组,o: 其它用户, a: 以上所有
- 更改属主
chown USERNAME FILENAME
- 共享文件
通过设置文件的组权限来共享
mkdir testdir
chgrp shared testdir // 把testdir的组设为shared
chmod g+s testdir // 给组设置sgid, 这样可以强制文件夹和子文件都属于shared组
Linux磁盘管理 (TODO: 暂时用不到,后续再细看)
- 文件格式
- 磁盘分区
- 逻辑卷管理
安装软件程序
- debian系统:dpkg
- apt-get
- aptitude
aptitude search package_name // 软件包前显示i, 表示已安装
aptitude install package_name
aptitude search package_name // 确认下是否安装成功
aptitude safe-upgrade // 更新软件包
aptitude purge package_name // 删除软件包
添加aptitude仓库, 编辑/etc/apt/sources.list文件来添加仓库
- 红帽系统:rpm
- yum
yum list installed // 列出已安装包
yum list installed xterm // xterm是否安装
yum provides file_name // 查看系统上特定文件属于哪个软件包
yum install package_name // 安装软件
yum localinstall package_name.rpm // 安装手动下载的包
yum list updates // 列出可更新的包
yum update package_name // 更新包
yum remove package_name // 删除软件包,但保留相关配置
yum erase package_name // 删除软件和它的所有文件
// 处理损坏的包依赖关系
yum clean all
yum deplist package_name
yum update --skip-broken
yum repolist // 列出yum的软件仓库地址
yum的仓库定义文件位于 /etc/yum.repos.d
- 从源码安装
tar -zxvf sysstat-11.1.1.tar.gz
cd sysstat-11.1.1
./configure
make
make install
AndreGeng commented
shell编程基础
- 输出信息
echo -n 'hi there' // 输出信息但不换行
- 变量
- 变量、等号和值之间不能出现空格
myvar=123
- 命令替换 // 从命令输出中提取信息,并将其赋给变量。把输出赋 给变量之后,就可以随意在脚本中使用了
+ 反引号字符(`)
+ $()符号, 这个相对backtick的优势是它比较方便嵌套
today=$(date +%y%m%d)
ls /usr/bin -al > log.$today
- 重定向输入和输出
- 输出重定向 e.g. date > test6
- 输入重定向 e.g. command < inputfile
- ps: 在命令行上,命令总是在左侧,而重定向符号“指向”数据流动 的方向。小于号说明数据正在从输入文件流向命令. e.g. wc < test6
- 内联输入重定向
- command << marker
wc << EOF
test string 1
test string 2
EOF
以上命令输出结果为:
2 6 28
ps: wc对数据中的文本进行计数,默认情况下它输出三个值
- 文本的行数
- 文本的词数
- 文本的字节数
- 管道
- command1 | command2 // 不要以为由管道串起的两个命令会依次执行。Linux系统实际上会同时运行这两个命令,在 系统内部将它们连接起来。
- 执行数学计算
- expr // e.g. expr 1 + 5
-
$[] // e.g. $ [1 + 5], deprecated way use $(()) instead -
$(()) // g.g. $ (( 1+ 5))
ps: bash shell数学运算符只支持整数运算。若要进行任何实际的数学计算,这是一个巨大的限制。
- 浮点解决方案
- bc, variable=$(echo "options; expression" | bc)
e.g. myvar = $(echo "scale=4;3.14/5" | bc)
- bc, variable=$(echo "options; expression" | bc)
- 退出脚本
- $? // 返回上一个命令执行的退出码
- exit // 指定程序的退出码,e.g. exit 5, 注意退出码要<=255
流程控制语句
判断
- if-then
if command; then
commands
fi
或者
if command then
commands
fi
- if-then-else
if command then
commands
else
commands
fi
- if-then-elif-else
if command1; then
commands
elif command2; then
more commands
fi
- test命令
- 基本语法
if test $my_variable; then
echo "The $my_variable expression returns a True"
else
echo "The $my_variable expression returns a False"
fi
或者
if [$my_variable]; then
echo "The $my_variable expression returns a True"
else
echo "The $my_variable expression returns a False"
fi
- 数值比较
if (( a > b )); then
...
fi
或者(不如上面的功能强大)
if [ "$a" -gt "$b" ]; then
...
fi
ps: 用中括号时,注意两边要有空格的.
- 字符串比较
str = str1 // 相等
str != str2 // 不相等
-n str // str长度是否为0
-z str // str长度是否为非0
if [ $val1 \> $val2 ]; then
echo "$val1 is greater than $val2"
fi
// 双括号提供了test不支持的特性:正则表达式
if [[ $USER == r* ]]; then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi
- 文件比较
-d file // 文件是否存在并且为目录
-e file // 文件是否存在
-f file // 文件是否存在且为文件
-r file // 文件是否存在且可读
-s file // 检查file是否存在并非空
-w file // 检查file是否存在并可写
-x file // 检查file是否存在并可执行
-O file // 检查file是否存在并属当前用户所有
-G file // 检查file是否存在并且默认组与当前用户相同
file1 -nt file2 // 检查file1是否比file2新
file1 -ot file2 // 检查file1是否比file2旧
- 复合条件测试
if [ -d $HOME ] && [ -w $HOME/testing ]; then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi
- case命令
// 语法
case variable in
pattern1 | pattern2) commands1;; pattern3) commands2;;
*) default commands;;
esac
// e.g.
case $USER in
rich | barbara)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
循环
- for
for test in Nevada "New Hampshire" "New Mexico" "New York"
do
echo "Now going to $test"
done
// 在Linux中,目录名和文件名中包含空格当然是合法的。要适应这种情况,应该将$file变 量用双引号圈起来。如果不这么做,遇到含有空格的目录名或文件名时就会有错误产生:./test6: line 6: [: too many arguments。
for file in /home/rich/test/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
// 更改字段分隔符, 默认字段分隔符为: 空格,制表符,换行符
file="states"
for state in $(cat $file)
do
echo "Visit beautiful $state"
done
// 指定多个字段分隔符
IFS=$'\n':;"
// c语言风格的for
for (( a=1, b=10; a <= 10; a++, b-- ))
do
echo "$a - $b"
done
- while
while test command; do
other commands
done
- until
until [ $var1 -eq 0 ]; do
echo $var1
var1=$[ $var1 - 25 ]
done
- 循环处理文件数据
IFS.OLD=$IFS
IFS=$'\n'
for entry in $(cat /etc/passwd); do
echo "Values in $entry –"
IFS=:
for value in $entry; do
echo " $value"
done
done
- break && continue
// break/continue 后面也可以跟要跳出的循环层级,e.g. break 2 //表示跳出两层循环,默认情况下是break 1
for (( var1 = 1; var1 < 15; var1++ )); do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]; then
continue
fi
echo "Iteration number: $var1"
done
- 处理循环的输出
for (( a = 1; a < 10; a++ ))
do
echo "The number is $a"
done > test23.txt
- 实例
input="users.csv"
while IFS=',' read -r userid name
do
echo "adding $userid"
useradd -c "$name" -m $userid
done < "$input"