AndreGeng/AndreGeng.github.io

Linux shell笔记

AndreGeng opened this issue · 1 comments

Shell基础

  1. 查看进程
  • ps -ef
  • top
  1. 杀死进程 kill pid
  2. 查看磁盘空间
  • df -h // 查linux服务器的文件系统的磁盘空间占用情况
  • du -h // 查看当前目录下各文件大小
  1. 处理数据文件
  • 排序
    • 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}'
  1. 归档与压缩
  • gzip
  • tar
    • tar -cvf test.tar test1.txt test2.txt
    • tar -xvf test.tar
    • tar -zxvf filename.tgz // 用于解压gzip压缩过的tar文件
  1. 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)+ 进程列表

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 // 列出现在所有设置的别名

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

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)
  • 退出脚本
    • $? // 返回上一个命令执行的退出码
    • 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"

处理用户输入