shell 基础

echo $str    与  echo  “$str” 的不同:

cmd $a  与 cmd “$a” 的不同:

如果$a=”a b”; 即:包含空格

前者: cmd接到的是2个参数

后者: cmd接到的是1个参数

举个栗子:如下图,我要把grep的那个关键字用变量替换:

  1. 直接 grep $keywords 测试是不行的,相当于grep 接收了两个参数
  2. 于是乎考虑拼凑第一条命令,就是把单引号拼上:
    grep ‘$kerwords’ 显然是不行的,因为单引号里面的变量是不被替换的
    grep “‘$keywords'” 似乎很完美了,不仅能替换变量,还能添加单引号;上图来看,结果没符合预期,且看下面测试:

    显然,这里的单引号参数值的一部分,而上图第一行的单引号仅仅是在形式化一个参数,是在解释阶段被处理的,至于是单引号还是双引号都关系不大;本质上,我们要保证的是替换后的变量要作为一个(而不是多个)参数传递给grep;从上面我们分析的 $a 和 “$a”的区别,我们便可以知道,其实直接grep “$keyword”就可以了
  3. 总结: 很多情况下,我们是要注意$var 最好写成 “$var” 的;我们经常想要的是 “$var” ,而不是 $var

cmd $a $b 与 cmd “$a” “$b”

前者如果$a为空,则 $1得到的将是$b的值,出现参数错位的情况;后者则不会出现这种情况

管道到read

a b 变量没有被成功赋值;
原因: 由于管道的存在,默认情况下,第一个管道的前面部分是在当前bash进程中执行的,后面的被管道隔开的命令都是在单独的子shell中执行的,那么是bash的内置命令(如:read)也要单启动一个bash进程来执行,而子进程中的变量不会对父进程产生影响,显然,例子中read a b里面的a b是子进程中的变量,在父进程中是不能echo出来的。

据说bash提供了一个选项lastpipe,可以改变bash的行为,使得管道链中的最后一个命令在当前bash中执行,其他的命令都在子shell中执行,不过我的bash目前是4.1(目前最新是4.4,还没stable),还没有这个选项,通过shopt查询是否有该选项。不过,据说使用该选项将不得不禁用作业控制。

还有其他办法:

办法1: 使用Here Document

办法二: 使用Here String

办法三: 使用Here Command

这个似乎应该叫做Process Substitution

参考: http://www.tldp.org/LDP/abs/html/process-sub.html

参考资料: http://stackoverflow.com/questions/13763942/bash-why-piping-input-to-read-only-works-when-fed-into-while-read-const

搜索: https://www.google.com.hk/?gws_rd=cr,ssl#safe=strict&q=bash+read+pipe+into+variable

关于here document、here string

here文档和here字符串不同于其他语言中仅仅用来定义复杂的字符串的,而是有特殊目的的,bash中的here文档和here字符串是用在命令后面的,他自动修改了命令的标准输入,如:

错误用法:

我们发现 变量a没有被成功复制。

hello world也没有成功被打印出来。

正确的用法:

等效于:

《Advanced Bash-Scripting Guide》中是这么说的:

A here document is a special-purpose code block. It uses a form of I/O redirection to feed a command list to an interactive program or a command, such as ftp, cat, or the ex text editor.

 

参考资料: http://www.tldp.org/LDP/abs/html/here-docs.html

关于 Here command

没有找见这方面的文档

关于字面量

对于 echo “$a”  大可不必担心 $a 有函数 双引号而在替换后破坏结构;如果a='”123′ , 则不必担心echo “$a” 会被处理成 echo “”123”

关于花括号扩展

花括号与元素之间不能有空格,元素之间逗号分隔且不能有空格

如果花括号中的元素含有变量,而变量中含有空格是没有问题的,但是空格元素可能会被丢掉,所以,为了不发生意外,变量最好用双引号引起来

 

$* vs $@

参考: http://c.biancheng.net/cpp/view/2739.html

https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

shell字符串处理

1. 取长度

2. 查找子串的位置

3. 选取子串

4. 截取子串

可以这样记忆, 井号(#)通常用于表示一个数字,它是放在前面的;百分号(%)卸载数字的后面; 或者这样记忆,在键盘布局中,井号(#)总是位于百分号(%)的左边(即前面)    🙂

(#用于卸掉前面部分,%用于卸掉后面部分; 出现一次是最短匹配,出现两次是最长匹配)

5. 字符串替换

# 用于前缀替换

%用于后缀替换

6. 比较

7. 连接

 

转自: http://www.cnblogs.com/frydsh/p/3261012.html

keyword:

bash,字符串,操作

docker 之 bash命令行自动补全

很多命令都会提供一个bash-complete的脚本,在执行该命令时,敲tab可以自动补全参数,会极大提高生产效率。docker亦如此,如:yum install docker后,会有一个文件: /usr/share/bash-completion/completions/docker ; 显然,该脚本是用于bash自动补全的,但是,不幸的是,直接 source 执行该脚本后,docker命令后敲tab,有如下错误:

难道docker的bash自动补全还能有问题?

放狗一搜,解决办法如下:

我这里虽然安装了bash-completion ,但是没有执行其中的一个文件:

该文件中有上面缺少的命令,自然,执行该文件就解决了。

其实 bash_completion 包中已经包含了一堆常用命令的bash自动补全的脚本的:

 

bash补全脚本编写:(实例)

 

complete -F func cmd 其中func的规则:

func可以接受到3个参数:

$0: 就是bash

$1: 命令名

$2: 需要补全的单词的一部分

$3: 上一个完整的单词 , 如果当前要补全的是第一个参数,则上一个完整的单词就是命令名

 

使用命令(任何语言)来实现命令补全(不过还是shell方便,实在不会shell就选择其他语言):

通过命令 mycomplete 给命令phpor来补全:

其中: mycomplete 命令将接到三个参数:

$1: 命令名(就是phpor)

$2: 需要补全的单词

$3: 命令行中出现的最后一个完整的单词

mycomplete要做的事情就是根据最后一个完整的单词来推测下个(要补全的)单词;只是如果命令行中已经包含了超过3个单词,则第二个单词信息将得不到; 其实不是的,只是从命令行参数中得不到,从环境变量是可以得到的,环境变量 COMP_LINE 保存了整个命令行的内容,命令行参数只是有助于我们定位光标的位置。


complete -C  和 -F 的说明

-C 指定一个命令; -F 指定一个函数 。 那么, -C 指定一个函数能行吗?  -F 指定函数一定能行吗?

其实:

  1. -C 也能指定一个函数, 这两个选项的本质不是函数或者二进制程序的命令; 而是传递信息的方式不同(未验证,有时间再验证)
    1. 如果指定-C ,那么会通过参数和export出来的环境变量来传递
    2. 如果指定-F, 那么会通过参数和特定的全局变量来传递

 


可以通过是strace -e file bash 来了解bash自动补全的逻辑,可以通过函数命令complete来设置自动补全函数,如果不存在自动补全函数的话,会自动在补全目录下搜索当前执行的命令为名称的文件,如  ~/.local/share/bash-completion/completions/byssh ,我们可以在这里添加自动补全逻辑

 

complete 的更多用法参考: man bash  搜索: Programmable Completion

如果我们需要补全一些常规得到东西,可以使用 -A ,如:

  1. 补全当前shell中的函数: complete -A function xxx
  2. 补全当前shell中的alias : complete -A alias xxx

参考资料:

Docker 存储

且看下docker info:

metadata一般不会太大,暂且不论。

  1. data默认107.4G (话说这还是centos7上的,曾经似乎只有10G),如何修改?
  2. 43个容器,57个镜像; 看来所有容器数据、和镜像数据都在这里了
  3. 从上面信息来看,设备数据已经满了,如下也能查看
  4. 镜像依赖:
    如果 image2是在image1上修改得到的,则image1无法先于image2被删除
  5. 容器依赖:
    如果有容器在使用某镜像,则该镜像无法被删除
  6. 删除文件后空间不会收缩
    创建一个大文件,空间膨胀;然后再删除大文件,空间不会缩回;但是,似乎会被重用这部分空间;问题:如何查看不能被重用的空间的大小呢?

 

问题:

一个docker存储空间被占满了:

  1. 如果扩容?
  2. 扩容必丢数据?如何备份数据?又如何导入数据?

 

关于loop-lvm 和 direct-lvm:

  1. loop-lvm不需要过多的配置,出于开箱即用的考虑,docker存储驱动默认使用的是loop-lvm,但是,非常不建议生产环境使用该方式(为什么?)
  2. direct-lvm配置比较复杂

docker存储空间扩容(不丢数据): https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/

docker在线扩容脚本: http://phpor.net/blog/post/3624

 

扩容注意事项:

扩容不要等到一点儿空间也没有的时候才做,否则后果有二:

  1. 要么进程申请不到存储资源,使得负载奇高,而且进程无法被杀死,只能重启docker宿主机
  2. 要么所有docker容器存储系统变为只读,只能重启docker daemon(所有容器都将需要重启)

参考资料:

  1. http://coolshell.cn/articles/17200.html
  2. https://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/
  3. https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/

 

shadowsocks vs vpn

话说有一个叫做影梭的app,可以基于shadowsocks(一下简称ss)在本地启动一个vpn,vpn流量走的是ss,相当安全;既然ss可以翻墙,也同样可以翻到内网;那么,ss能替代vpn吗?

下面讨论ss与vpn的一些区别:

  1. 账号
    1. ss每个server只有一个共享密码,没有用户名的概念;
    2. vpn可以有多个用户名、密码
  2. 验证
    1. 虽然ss有密码,但不做验证,其实是一个共享秘钥,直接用于数据加密;就算秘钥错了,server端也不做验证(或者无法验证,或者未提供验证逻辑),所以,使用ss时,密码错误是没有提示的
    2. vpn有密码验证阶段
  3. 如果非要把ss当做vpn来使用,则
    1. 充其量也就是个单用户的vpn
    2. ss client端需要实现虚拟网卡部分的功能,可能工作量比较大一些,好在影梭app已经这么做了(没发现类似的桌面软件或命令行软件)
  4. 鉴于ss的代码并不复杂,似乎可以给ss添加对读用户支持的验证逻辑的;相信ss距离vpn远不止这些,试图把ss改造成vpn可能不是一个对的想法

存储故障导致的

从来没想到过,存储故障能导致系统必须重启才能解决问题,尤其是,必须断电重启,reboot都不好使。

为什么呢?

存储故障导致进程因处于等待资源状态而处于D状态,该状态无法响应任何信号,即使 -9 信号也不好使。此种场景一般来讲dmesg能看到错误信息,如下图所示:

默认当等待资源超过120s时,内核会打印错误如下错误信息。

具体可能出现很多表现,如: top 、ps、ls 等卡死; 甚至访问/proc目录下的东西(如: /proc/xxx/cmdline ) 都能卡死,原以为/proc 下的东西都在内存中,其实完全不是,/proc 根本就是个接口

 

还有比reboot -f都需要等待更悲催的吗?

参考文章:

https://rachelbythebay.com/w/2014/10/27/ps/

http://baike.baidu.com/link?url=-dJFqkX4THUzLRJ9MtVJr1utLBa8tYn9x_APkLF_JvC-p57waIjtQ1HxlPJLuJGggYE0PZGCQ-h0BOftzW2eua

如何释放linux系统的cache

我们知道,linux会将文件尽可能的cache到内存里面,系统运行一段时间后,我们发现内存几乎被用完了,其实大部分是cache;而且,说实在的,真的没有必要去清理这个cache,内存真的不够用的时候,这些cache会自动释放的。

你们我们这人就是洁癖,就想把这些cache从内存中请出去,该用哪个命令呢?

我没有找到使用哪个命令,但是dd和rm可以做到:

  1. 如果cache使用了N个G,你们可以用dd创建一个N个G的大文件;这时候cache中的东西就被这N个G的文件给挤出去了
  2. 使用rm删除这个N个G的大文件;这时候cache中的这个N个G的大文件就被释放掉了,然后cache就空了,对应的内存就free了

 

通过drop_cache 实现:

 

Docker之Volume

情景

出于某种目的,docker容器挂在了外部的文件,而且期望当外部文件发生变化的时候,docker容器对应的文件也发生变化。

难道本来不是这样的吗?是也不是:

是:  echo “append info” >>/the/file    立即反应到docker容器内部了

不是: 通过vim编辑,添加一行,docker容器内部文件没有变化

分析: 应该是echo和vim有不同

当不知道的时候,结果就是: 有时候可以,有时候不可以,恶心不恶心

放狗搜索,这里说的详细: https://github.com/docker/docker/issues/15793

总结一下:

  1. docker (或者说linux mount)挂卷是基于inode的
  2. echo 是在原文件基础上修改文件的,没有改变文件的inode
  3. vim 编辑的时候改变了文件的inode; (上面文章有说,可以通过 set noswapfile来禁用此特性,测试不好使,字面来看也不太是这么回事儿,实际测试应如此设置: set backupcopy=yes)
  4. 其他编辑器也可能改变inode,有些也可以设置不改变inode
  5. sed 编辑文件默认也是修改inode的,可以使用-c选项避免inode发生变化

有解决办法吗?

只要想办法确保inode不变即可,如: 编辑文件的时候,先copy走,编辑完再cat 到原文件中。

什么? 你使用vim修改挂载的文件之后,docker容器中的文件立刻就变化了?

先确认下该文件的inode是否变化了(我不想去证明vim修改文件肯定会导致inode发生变化)

 

还有别的解决办法吗?

  1. 请确认必须挂在文件而不是也可以挂在目录。挂载目录的话,docker内外是完全同步的
  2. 如果挂载目录的话,目录中的软连接只能在挂载的目录内有效,如果软链的是其它目录的文件,则容器内外是不同的

关于挂载目录

如果容器内挂载了宿主机目录/home/phpor/test 到容器内部 /test , 现在创建目录 /home/phpor/test/taoyi ;然后在另一个终端容器进入容器/test/taoyi 目录, 这时候,如果宿主机上mv /home/phpor/test/taoyi /home/phpor/taoyi , 然后 创建文件 /home/phpor/taoyi/file ; 这时候,在另一个终端下对于原本已经进入 /test/taoyi 的回话将可以看到file文件