Linux下一个有趣的文件夹软链接

当你cd了半天,你肯定在想,我靠,是不是中毒了,谁TMD搞的这么多层级的文件夹,当你ls一看,你会说句: 我靠!

 

另:已经再工作了,虽然不是自己想要的事,但毕竟还是做程序。

bash+expect来处理下载完关机

昨天在用ff从tgbus上下载几个psp游戏,然后在想定个时在什么时候关机

sudo shutdown -h +320

但是一想,如果这破电信的网速不好,那岂不是白搞了(不知115是否能断点续传),然后就想到搞个下载完关机的sh脚本。

这里sh脚本我就不说了,思路是:

while循环

    sleep两秒

    如果测试没有以part结尾的文件名的话

        关机

想到这些我就写下了这样的东东

1
2
3
4
5
6
7
#!/bin/bash
while $(sleep 2);
do
    if [ ! -f *.part ]; then # 测试当前目录下有无firefox未下载完的文件
        sudo shutdown -h now
    fi
done

后来觉得有一问题,就是要管理员的密码。进而就想到了使用expect, 然后就把shutdown那句放入了命名为power-off.expect文件中。

power-off.expect
1
2
3
4
5
6
#!/usr/bin/expect
set timeout 60
spawn sudo shutdown -h +1 # 推迟1分钟,以防文件还未写完。
expect "leon:" # 这句是猜测执行上段命令回显的最后几个字符,大概就是"password for user:"或 "Password:"或"密码:"
send "yourpassword\r"
interact

注意:这里的power-off.expect里面你的密码就是明文了,一定要注意权限,我是直接700了(即同组与其它组无读,无写,无执行权限)。

然后,将power-off.expect放入要调用的执行脚本,即

power-off_after_downloading.sh
1
2
3
4
5
6
7
#!/bin/bash
while $(sleep 2);
do
    if [ ! -f *.part ]; then # 测试当前目录下有无firefox未下载完的文件
        ./power-off.expect
    fi
done

 

一个查看阴历的小工具

接着上一篇liblunar的日历bug问题,这里写了一个查询月份的工具,代码比较简陋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * author:pallover#gmail.com
 */
 
#include <lunar/lunar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
 
 
int str2int(const char *str)
{
    int n = 0;
    while(*str >= '0' && *str <= '9')
    {
        n = n * 10 + (*str - '0');
        str++;
    }
    return n;
}
 
int get_weekday(int year, int month, int day)
{
    int m = month;
    if(m == 1) {
        m = 13;
        year -= 1;
    }
    else if(m == 2) {
        m = 14;
        year -= 1;
    }
 
    int c = year / 100;
    int y = year % 100;
    int d = day;
 
    return (((c >> 2) - (c << 1) + y + (y >> 2) + (13 * (m + 1) / 5) + d - 1) % 7 + 7) % 7;
}
 
int get_mdays(int year, int month)
{
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30;
        case 2:
            if(!(year % 4) && year % 100 || !(year % 400))
                return 29;
            else
                return 28;
    }
}
 
int main(int argc, char *argv[])
{
 
    struct tm *tm_ptr;
    time_t the_time;
    (void) time(&the_time);
    tm_ptr = localtime(&the_time);
 
    int nyear = 0, nmonth = 1, nday = 1;
    if(argc > 1) {
        nyear = str2int(argv[1]);
        if(nyear <= 1900) nyear = tm_ptr->tm_year + 1900;
 
        if(argc > 2) {
            nmonth = str2int(argv[2]);
            if(nmonth < 1) nmonth = 1;
 
            if(argc > 3) {
                nday = str2int(argv[3]);
                if(nday < 1) nday = 1;
            }
        }
    }
    else {
        nyear = tm_ptr->tm_year + 1900;
        nmonth = tm_ptr->tm_mon + 1;
        nday = tm_ptr->tm_mday;
        printf("%d, %d, %d\n", nyear, nmonth, nday);
    }
 
    lunar_init(&argc, &argv);
    LunarDate *lunar_date = lunar_date_new();
 
    lunar_date_set_solar_date(lunar_date, nyear, nmonth, nday, 1, NULL);
    gchar *str = lunar_date_strftime(lunar_date, "%(NIAN)年%(YUE)月%(RI) 生肖:%(shengxiao) \n节日:%(jieri)");
    printf("\n%*s%s%d年%d月%d日%s\n\n", 20, "", "\033[47m\033[31m", nyear, nmonth, nday, "\033[0m");
    printf("农历:%s\n\n", str);
 
    char *weekday = "日一二三四五六";
    char ch[4];
    while(*weekday){
        strncpy(ch, weekday, 3);
        ch[3] = 0;
        weekday += 3;
        printf("%*s%2s", 6, "", ch);
    }
 
    int i = 1, line, nlen, n, firstwday, endline = 0;
    int nmdays = get_mdays(nyear, nmonth);
    firstwday = get_weekday(nyear, nmonth, 1);
 
    printf("\n");
    line = 7 - firstwday;
 
    while(nmdays > 0)
    {
        if(line % 7 && !endline)
            printf("%*s", firstwday << 3, "");
 
        for(nlen = 0; nlen < line; nlen++, i++) {
            n = get_weekday(nyear, nmonth, i);
            printf("%*s%s%2d%s", 6, "", ((i == nday) ? "\033[36m\033[1m" : ""), i, ((i == nday) ? "\033[0m" : ""));
        }
 
        i -= line;
        if(n == 6 || endline) {
            printf("\n");
        }
 
        if(line % 7 && !endline)
            printf("%*s", firstwday << 3, "");
 
        for(nlen = 0; nlen < line; nlen++, i++) {
            lunar_date_set_solar_date(lunar_date, nyear, nmonth, i, 0, NULL);
            printf("%*s%s%4s%s", 4, "", ((i == nday) ? "\033[36m\033[1m" : ""), lunar_date_strftime(lunar_date, "%(RI)"), ((i == nday) ? "\033[0m" : ""));
 
        }
        if(n == 6 || endline) {
            printf("\n\n");
        }
 
        nmdays -= line;
        line = (nmdays >= 7) ? 7 : nmdays % 7, endline = 1;
    }
 
    lunar_date_free(lunar_date);
 
    exit(0);
}

可以传入0-3个自己想查询有效参数(年,月,日),如不输入,则默认为本地日期。

liblunar的调用节日的bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
if (strstr(format, "%(jieri)") != NULL)
{
    gchar bufs[128];
    gchar *tmp;
    tmp = lunar_date_get_jieri(date, " ");
    if (*tmp)
    {
        g_utf8_strncpy(bufs, tmp, g_utf8_strlen(tmp, 128));
    }
 
    /*if (strstr(tmp, " " ) != NULL)
    {
        char** buf = g_strsplit(tmp, " ", -1);
        if (g_utf8_validate(*buf, -1, NULL))
            g_utf8_strncpy(bufs, *buf, 3);
        else
        {
            strncpy(bufs, *buf, 4);
            bufs[4]= '\0';
        }
        g_strfreev(buf);
    }
    else
    {
        if (g_utf8_validate(tmp, -1, NULL))
            g_utf8_strncpy(bufs, tmp, 3);
        else
        {
            strncpy(bufs, tmp, 4);
            bufs[4]= '\0';
        }
    }*/
    g_free(tmp);
    str = g_string_replace(str, "%(jieri)", bufs, -1);
}

好吧,本意是要拆分开然后再组在一起,却写的莫名其妙的,应该用循环也没用,就改了下。这下可以显示全了。

测试的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <lunar/lunar.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
    //g_type_init(); 这句我扔到lunar_init函数里面去了。
    lunar_init(&argc, &argv);
    LunarDate *lunar_date = lunar_date_new();
    lunar_date_set_solar_date(lunar_date, 2011, 10, 28, 1, NULL);
    gchar *str = lunar_date_strftime(lunar_date, "%(NIAN)年%(YUE)月%(RI)日%(SHI)时 生肖:%(shengxiao) 节日:%(jieri)");
 
    printf("out str:%s", str);
    lunar_date_free(lunar_date);
    exit(0);
}

 

Bash命令行历史权威指南

Bash命令行历史权威指南

首先,让我们回顾下一些基本的历史命令快捷键

你可能记得,Bash提供了两种编辑命令的模式:emacsvi, 很多快捷键在不同的编辑模式下是不同的。

切换模式:

$ set -o mode (vi/emacs)

假设你要执行如下命令:

$ echo foo bar baz
$ iptables -L -n -v -t nat
$ ... lots and lots more commands
$ echo foo foo foo
$ perl -wle 'print q/hello world/'
$ awk -F: '{print$1}' /etc/passwd
$

然后你想执行最后一条历史命令(awk -F ...).

你会想当然的按下键盘的上箭头并切安逸地陪伴你一生,但是真的需要把你的手移动哪么远吗?

如果在Emacs模式下你只用按下Ctrl-P就能够从历史记录中取到前一条命令(Ctrl-n取下一条命令)

vi模式下你只要按下Ctrl-[(ESC)(用来切换到命令模式)与‘h’('j'取下一条命令)即可

这儿有一个等同有效的方式就是Bash的历史扩展机制--事件引用符, 输入“!!”就可以达到以上等同的效果(详细后面说明)

现在,假设你不想通过再次重复输入就想执行以上第二条命令'iptables -L -n -v -t nat'

天真的用户会照常按下键盘上箭头直到找那所需的那条命令,但这并不是黑客们的玩转方式,黑客们喜欢快速有效的方式。忘掉键盘的上下右左键以及翻页那一块键吧,依我看,它们跟本用不着,而且离键盘主要工作区域太远了。(看来作者应该玩的是vi

emacs模式下输入‘iptables’前几个字符,然后使用Ctrl-r就行了,比如:'ipt', 这将会正确地得到最后的以'iptables'起始的命令,再次输入Ctrl-r会得到更老输入的匹配,假如你错过了正确的结果,你要通过Ctrl-s就可以反向的搜索了(不要忘记默认的Ctrl-s操作会停止终端的输入,看起来像是假死了,记得按下Ctrl-g让终端恢复正常,具体设置,参见‘sttycommand’)。

vi模式下使用Ctrl-rCtrl-s会有些不同。

通过使用‘Ctrl-[’或‘ESC’结合'/'来切换到命令模式,输入‘iptables’前几个字符(插入模式),如'ipt',然后回车. Bash将会匹配到最近的一条以'ipt'开始的历史记录,输入‘n’或者通过'/'再次操作会得到相关的上一条命令,使用 'N' or '?' 得到下一条命令。

通过事件引用符可以得到最最近以‘string’开头的历史命令

而使用'!iptables'就能扩展到最近的以‘iptables’开头的历史命令

另一种方式是使用Bash自身的‘history’命令与grep结合,然后再使用事件引用符‘!N’, N指的是历史记录中的第几条命令。

例如:

$ history | grep 'ipt'
  2    iptables -L -n -v -t nat
$ !2     # will execute the iptables command

vi编辑模式下,使用‘N’(命令行号)与‘G’, 就可以达到相同的效果,此例使用'2G'即可

列出与擦除历史命令

Bash内建了历史命令('history')的查看与擦除

及以下例为例:

$ echo foo bar baz
$ iptables -L -n -v -t nat
$ ... lots and lots more commands
$ echo foo foo foo
$ perl -wle 'print q/hello world/'
$ awk -F: '{print$1}' /etc/passwd
$

输入‘history’将会得到所有的带行号的历史命令

1    echo foo bar baz
  2    iptables -L -n -v -t nat
  ...  lots and lots more commands
  568  echo foo foo foo
  569  perl -wle 'print q/hello world/'
  570  awk -F: '{print$1}' /etc/passwd

输入‘history N’(N为整数),会得到最近的N条历史命令,如:'history 3', 结果如下:

568  echo foo foo foo
  569  perl -wle 'print q/hello world/'
  570  awk -F: '{print$1}' /etc/passwd

history -c’将清除所以的历史记录,'history -d N'将会删除N条历史记录

默认在用户的主目录下会有一个名为'.bash_history'的历史记录文件

历史命令扩展

历史命令扩展的完善就是依赖所谓的事件引用符与词组引用符,事件引用符适用于先前取得的命令(事件),词组引用符适用于由事件而来的命令行。我们可以选择的就是:多样的修饰符可应用于已提取的参数。

事件引用符都是的以感叹号开头的特殊命令(也有以“^“开头的),它们可跟一个词组引用符和一个或多个修饰符。引用符与修饰符都是由':' 分隔开的

事件引用符

让我们看看下面的一组事件引用符的例子是如何运作的。

事件引用符“!!”用来取得前一条命令,例如:

$ echo foo bar baz
foo bar baz
$ !!
foo bar baz

这里的“!!”就执行了'echo foo bar baz'这条命令

事件引用符'!N'用于执行命令历史的第N条记录

假如你的历史记录输出如下:

1    echo foo foo foo
  2    iptables -L -n -v -t nat
  ...  lots and lots more commands
  568  echo bar bar bar
  569  perl -wle 'print q/hello world/'
  570  awk -F: '{print$1}' /etc/passwd!

!569”就会执行'perl …' 这条命令,而'!1'就会执行'echo foo foo foo'

 

'!-N'就会执行当前的命令的计数前N条命令(中间的‘-’可以理解为减号),如下:

$ echo foo bar baz
foo bar baz
$ echo a b c d e
a b c d e
$ !-2
foo bar baz

!-2’执行上上一条命令,或者说是当前的命令的前2条命令

'!string'适用以'string'开头的历史命令。例如:

$ awk --help
$ perl --help

!p‘或者'!perl'或者'!per'都可以正确是执行到'perl -help', 同理,'!a'亦可以执行那条以awk开头的命令

'!?string?'就能匹配到包含'string'的命令,并不严格要求开头。

最有意思的事件引用符莫过于'^string1^string^'了,它将替换最后一条命令中的'string1'字符窜为'string2',例如:

$ ehco foo bar baz
bash: ehco: command not found
$ ^ehco^echo^
foo bar baz

上面的‘^ehco^echo^’作用就是取得上一条历史记录并替换ehcoecho,然后再执行

词组引用符与修饰符

事件引用符后面词组引用符(一个或多个)是用冒句分隔开的,它们适用于当前参照命令的部分或者所有的参数

例如:

$ echo a b c d e
a b c d e
$ echo !!:2
b

这里有一个最简单的词组引用符,':2'对应第2个参数(第3个单词),通常情况下':N'对应命令的第N-1个单词

词组引用符也接受一个范围值,例如:

$ echo a b c d e
a b c d e
$ echo !!:3-4
c d

符号用法也是多样的,例如,”:$”对应最后一个参数,”:^”对应第一个参数,”:*”对应所有的参数(等同':1-$'), 等等,详情看完整的参照表

修饰符可以改变单词引用符的行为,例如:

$ tar -xvzf software-1.0.tgz
software-1.0/file
...
$ cd !!:$:r
software-1.0$

'r'在这里就匹配了上一条命令的最后一个参数,'r'在此用作去掉后缀'.tgz'

h’用于匹配并删除文件路径中的文件名部分,返回文件路径的开头部分:

$ echo /usr/local/apache
/usr/local/apache
$ echo !!:$:h
/usr/local

'e'则只保留扩展名

$ ls -la /usr/src/software-4.2.messy-Extension
...
$ echo /usr/src/*!!:$:e
/usr/src/*.messy-Extension    # ls could have been used instead of echo

另一个有趣的修饰符是替换符':s/old/new/', 即替换单词'old''new''g'用于全局替换,例如:

$ ls /urs/local/software-4.2 /urs/local/software-4.3
/usr/bin/ls: /urs/local/software-4.2: No such file or directory
/usr/bin/ls: /urs/local/software-4.3: No such file or directory
$ !!:gs/urs/usr/
...

以上这个例子就是替换所有的”urs”'usr'来达到我们想要的结果。

这里还有一些其它的修饰符,如'p'只是用来回显扩展过的命令,并不执行。详情看完整的参照表。

配置命令历史

Bash允许用户自己配置命令历史文件,文件保存着命令以及记录号和一些选项

变量 HISTFILE, HISTFILESIZE, HISTIGNORE HISTSIZEenvironment 对命令历史起着决定的作用。

HISTFILE, 正如词面意思,代表命令历史文件的路径

$ export HISTFILE=/home/pkrumins/todays_history

上面命令当保存命令历史文件路径为'/home/pkrumins/todays_history'

值设置为'/dev/null'或空将不保存历史记录

HISTFILESIZE 控制着历史文件最大记录数,例如:

$ export HISTFILESIZE=1000

这将保存1000条最近的历史命令

HISTSIZE 控制着当前会话(终端)的历史记录条数

$ export HISTSIZE=42

上面将保存当前会话最近42条历史命令

如果HISTSIZE变量的值小于HISTFILESIZE变量的值,只有限定数量的命令会被记录下来

HISTIGNORE 用来控制我们不想记录的命令,此变量是用冒号来分隔不同的模式,'&' 对匹配历史命令起来特殊的作用。

[ ]*’这个技巧可以用来忽略以空格开头的命令。

例如:

$ export HISTIGNORE="&:[ ]*:exit"

上面的命令就控制bash忽略复制命令,以及以空格开头,以及内容为‘exit’字符的命令

There are several other options of interest controlled by the built-in 'shopt' command.

这儿有几个有关‘shopt’命令的选项。

'-s'参数让'shopt'命令的配置有效,而'-u'参数使其无效。

histappend’控制着历史列表如何写到命令历史记录中,设置这个选项将追加当前会话的历史命令到命令历史记录中,不设置(默认不设置,我的ubuntu 10.04默认却是打开的)每次将会重写命令历史记录文件。

打开:

$ shopt -s histappend

关闭:

$ shopt -u histappend

选项'histreedit'允许用户重编辑一条有误的历史替换命令

试想你已经输入如下:

$ echo foo bar baz

本意想通过'^baz^test^'替换'baz''test', 但是却输成了'^boo^test^', 这将因为不匹配而导致替换失败

如果将此选项打开,bash将会保持你当前的'^boo^test^'错误的输入

最后,选项'histverify'允许用户去验证一条替换历史扩展命令

就拿前面的例子来说吧,假设你想通过'!!'再次'echo'命令,如果这个选项打开,bash将不会立即执行这条'echo'命令,而是将命令显示好等待你去验证是否是你想要的结果。

调整命令提示

我的命令提示是这样的:

Wed Jan 30@07:07:03
pkrumins@catonmat:1002:2:~$

第一行显示日期与时间可以及时的追溯命令

第二行显示用户名,主机名,全局行号以及当前命令当号

全局行号可以让我更快捷地使用事件引用符

我的PS1参数,变量值如下:

PS1='\d@\t\n\u@\h:\!:\#:\w$ '

Bash历史技巧表

在此下载(英文)

 

原文:http://www.catonmat.net/blog/the-definitive-guide-to-bash-command-line-history/

 

首次翻译技术文章,如有不明白之处,敬请谅解,欢迎各位来我的博客,提出您的意见

逍遥云的博客: http://hi.baidu.com/pallove

pdf提供下载:http://pallove.is-programmer.com/user_files/pallove/Image/Bash命令行历史权威指南.pdf