7
9
2013
4

把标准输出伪装成终端

fcitx-diagnose 是 fcitx 输入法的非常优秀的诊断脚本。当输出到终端时,fcitx-diagnose 会给输出加上易于区分不同类型的消息的彩色高亮。可是,当用户把输出重定向到文件以便让其他人帮助查看时,这些高亮就没了。fcitx-diagnose 的输出很长,但如果通过管道给 less 查看的看,这些彩色也会消失。

要是 fcitx-diagnose 支持--color=always这样的选项就好了。可是 yyc 说他懒得写。getopt我只在 C 里用过,好麻烦的,所以我也懒得写。于是,我还是用我的 ptyless 好了。后来又想到,用于改变 I/O 缓冲方式的 unbuffer 和 stdbuf 应该也可以。测试结果表明,只有 unbuffer 可行,因为它是和 ptyless 一样使用伪终端的。stdbuf 则是使用 LD_PRELOAD 载入一个动态链接库的方式来设置缓冲区。

不过,既然 stdbuf 用 LD_PRELOAD 来设置缓冲区,我何不来用相同的办法改变isatty()函数的返回值呢?同时,我也学学 stdbuf,试了下__attribute__ ((constructor))指令。

#include<stdarg.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<dlfcn.h>

static int (*orig_isatty)(int) = 0;

int isatty(int fd){
  if(fd == 1){
    return 1;
  }
  return orig_isatty(fd);
}

void die(char *fmt, ...) {
  va_list args;
  va_start(args, fmt);
  vfprintf(stderr, fmt, args);
  va_end(args);
  fprintf(stderr, "\n");
  fflush(stderr);
  exit(-1);
}

__attribute__ ((constructor)) static void setup(void) {
  void *libhdl;
  char *dlerr;

  if (!(libhdl=dlopen("libc.so.6", RTLD_LAZY)))
    die("Failed to patch library calls: %s", dlerror());

  orig_isatty = dlsym(libhdl, "isatty");
  if ((dlerr=dlerror()) != NULL)
    die("Failed to patch isatty() library call: %s", dlerr);
}

然后,像 stdbuf、proxychains 那样做了个包装,不用自己手动设置 LD_PRELOAD 环境变量了。这也是我第一次使用 CMake,比 GNU 的 autotools 那套简单多了 :-)

使用方法很简单:

  1. 克隆或者下载源码
  2. 编译之
    $ mkdir -p build && cd build
    $ cmake .. # 或者安装到 /usr 下: cmake .. -DCMAKE_INSTALL_PREFIX=/usr
    $ make
    
  3. 安装之
    $ sudo make install
    $ sudo ldconfig
    
  4. 可以使用了:
    $ stdoutisatty fcitx-diagnose | less
    
Category: Linux | Tags: C代码 linux 终端 shell
4
23
2013
7

使用 sed 来切换 hosts 文件项

工作中经常会需要将一个域名映射到本地以调试,但是其余时间又需要让其正常解析。手工修改/etc/hosts文件烦耶!

于是有了以下脚本:

#!/bin/bash

if [[ $UID -eq 0 ]]; then
  sed -i '/^#127\.0\.0\.1\s\+example\.org/s/^#//;t;/^127\.0\.0\.1\s\+example\.org/s/^/#/' \
    /etc/hosts
  systemctl restart dnsmasq
else
  grep -m1 -F 'example.org' /etc/hosts
fi

使用 sed 是因为觉得没必要用 awk 这样复杂的东西,也正好更深入学习下 sed。此代码中用到了t命令,它的语义是:

当当前行的上一个s命令成功(至少替换一次)时,跳转到指定的标签。如果没有指定标签,则跳转到脚本尾部。上边的命令中,当example.org域名这行被注释掉时,s命令成功去掉其前的注释符,然后t命令跳过后边加注释符的s命令,到达脚本尾部。

标签使用冒号定义。以上命令使用标签时如下所示:

  sed -i '/^#127\.0\.0\.1\s\+example\.org/s/^#//;te;/^127\.0\.0\.1\s\+example\.org/s/^/#/;:e' \
    /etc/hosts

当然,以上脚本还做了另一件事:当以普通用户身份执行时,不修改 hosts 文件,而是显示相关行以查看状态。

Category: shell | Tags: sed shell
3
19
2012
20

zsh 命令行编辑技巧三则

zsh 的命令行编辑使用的是 Zsh Line Editor(Zle),功能比 readline 强大不少,只是大量好用的功能都深埋于文档中,难得见识到。最近在看A User's Guide to the Z-Shell,虽然内容有些旧了,但依旧很有用。

首先说一点,以下内容均假定使用的是 Emacs 式键绑定。

暂停当前命令的编辑,先执行点其它命令。这个功能叫push-line,默认绑定在Alt-q。另有一个叫做push-line-or-edit的 widget,我把它绑过来了:

bindkey "\eq" push-line-or-edit

push-line widget 会将当前命令行上的内容放到一个栈上,显示一个新的提示符让你来执行点别的东西。比如刚写了一个长命令的一半,却发现当前目录不对。怎么办呢?readline 里我只好先Ctrl-u,执行之后再Ctrl-y粘贴回来。偶尔会找不到之前 kill 的内容。在 Zsh 里,按下Alt-q,当前命令暂存起来,你可以执行点别的命令,再显示命令提示符时,之前 push 走的命令内容会 pop 回来。而且这个操作是可以嵌套的,因为这是一个

push-line-or-edit widget 多了个 or-edit 后缀。当输入一个if或者for这样的命令时,你可以写成多行,zsh 会自动判断出你的命令尚未写完,显示$PS2提示符。这时,如果想修改之前的某一行怎么办呢?push-line-or-edit widget 会把这些行命令变成一个不带有$PS2提示符的多行命令,默认键绑定中,使用Ctrl-p/n或者方向键移动即可。这个就是 zsh 的多行编辑能力。如果你喜欢使用 zsh 编辑的话,可以试试zed这个运行于 zsh 中的简单文本编辑器:

autoload zed
zed some_small_text_file

按顺序执行若干条历史记录中的命令。比如我读取 3G 网卡短信使用如下的命令序列:

gnokii --smsreader
gnokii --getsms SM 0 end -f sms
smsmboxproc < sms > sms.mbox
mutt -f sms.mbox

如果使用Ctrl-r搜索历史的话,每条命令都搜索岂不麻烦?所以有了accept-line-and-down-history这个 widget,默认绑定于Ctrl-o。先在历史记录里找到第一条需要的命令,按下Ctrl-o,命令执行后,历史记录中的下一条就会出现了。然后接着按Ctrl-o,直到需要执行的命令序列到达最后一条,这次该按Enter了。

最后一个,你是不是经常往命令行上粘贴网址?是的话,你应该知道,网址得用引号括起来,以防止有些字符被 shell 解释了。zsh 带了个功能,可以检测出当前输入的是否是 URL,如果是的话就自动转义那些特殊字符。这样往命令行上粘贴 URL 时就不需要事先打好引号了。使用如下命令启用:

autoload -U url-quote-magic
zle -N self-insert url-quote-magic
Category: shell | Tags: zsh shell
10
24
2010
4

把Parted Magic装到移动硬盘

首先,不知道Parted Magic为何物的童鞋请先移步这里

之前折腾了Gparted live,然后看到了muzuiget童鞋的留言,决定有时间还是折腾下。首先去官网,结果官网说

As of Parted Magic version 5.3 the USB zip has been discontinued. We are only supporting Unetbootin as the method for booting Parted Magic from a USB drive.

Note: If you still want to do it manually, extract the files from the ISO image and execute the syslinux commands. If you know what you are doing this shouldn't be an issue.

好吧,我在看到第一段,在失望之余,“note”到了后面那段。

fuseiso ~/dl/pmagic-5.5.iso pm

我不喜欢需要根用户权限的mount,所以有一天在新立得里翻到了这个fuseiso,现在用上了。

里边就两个目录,boot和pmagic。pmagic比较简单,先说说它。

它下面也就三项,内核bzImage,initrd文件initramfs,和一个可以用来自定义其模块的目录pmodules,目前只包含pmagic-5.5.sqfs,file之可以知道它是一个Squashfs filesystem。

boot下东西有点多,还好我需要的很少,就是boot/isolinux/isolinux.cfg这个isolinux的配置文件了,里面有需要传给内核的参数,有好几个选项,基本上是这样子的:

LINUX /pmagic/bzImage
INITRD /pmagic/initramfs
APPEND edd=off noapic load_ramdisk=1 prompt_ramdisk=0 rw vga=788 loglevel=0 max_loop=256

APPEND那行就是内核的参数了。于是,grub2的参数应该这么写:

menuentry "Parted Magic (中文)" {
	set root="(hd1,1)"
	echo "Loading kernel..."
	linux /boot/pmagic/bzImage edd=off noapic load_ramdisk=1 prompt_ramdisk=0 rw vga=791 loglevel=0 max_loop=256 keymap=us zh_CN
	echo "Loading initrd..."
	initrd /boot/pmagic/initramfs
}

vga参数我自然要改下的,呵呵。

但是——

内核加载完毕之后它就告诉我找不到pmagic-5.5.sqfs文件……看来需要把它放到分区的/pmagic/pmodules下才行啊。可是我又不是做LiveUSB,是移动硬盘耶,不可能随随便便就把整个分区弄乱的。

于是——

file一下那个initramfs可知它是gzip压缩过的。

zcat initramfs > ../../initrd

然后继续file之,cpio文件。那就继续解包吧:

cat initrd|cpio -i

其实两步是可以一起做的。但没有file以及man cpio之前谁知道呢。

然后我很惊奇地看到一个名为init的可执行文件躺在当前目录下!我都没file,直接把它扔给Vim了。接下来就是看shell脚本了。

find_pmagic () {

   if [ ! -e /test_mnt/${directory}/pmagic/pmodules/pmagic-$VERSION.sqfs ]; then
      umount /test_mnt &> /dev/null
      return
   fi

关键是${directory}变量。继续寻找。。。

for i in $(cat /proc/cmdline); do
   case $i in
      directory=*)     directory=$(get_opt $i) ;;
      iso_location=*)  iso_location=$(get_opt $i)  ;;
      iso_filename=*)  ISO_VERSION=$(get_opt $i) ;;
      root=*)          root=$(get_opt $i)  ;;
      label=*)         label=$(get_opt $i)  ;;
      uuid=*)          uuid=$(get_opt $i) ;;
   esac
done

OK,原来是内核参数,于是在linux那行后面再加上directory=other,重启OK!

6
10
2010
0

多篇Word课件打印,我这样省纸

我需要六个共21页Word文档的课件以供开卷考试时使用。可是打印成21页也太浪费了!字号可不需要那么大,于是使用Ooo调整字号,结果令我郁闷的是,除了大部分文字位于左边而右边很多空白之外,我发现项目符号依旧是很大的字号。这样可不行。而且,一个个文件处理太没效率——我马上就要去应考了啊!考虑过LaTeX,结果因为不会调字体而放弃。于是想起了前些天发现的Google Docs使用的prince。于是开始工作——

首先把Word转成txt。我使用的是wvText这个位于wv包的工具。只能一个个文件转,不过我使用的是shell脚本(因为要手动给那些Word排序),这个就没关系了。然后把内容cat到一起,成为一个大的纯文本文件。然后打开这个文件,用Vim删去空行,然后在利用正则表达式\v第.章|^(一|二|三|四|五|六|七|八|九|十)加上Vim的宏给标题前空一行免得到时候不好找答案。prince是处理HTML的,所以还要把它改成HTML。这个手动就可以了,写上head标签,meta指明编码,正文前后加上<pre>标签,再写点样式表:

pre {
  font-size: 11px;
  font-family: 'Adobe Song Std';
  -moz-column-count: 2;
  -moz-column-gap: 20px;
  -webkit-column-count: 2;
  -webkit-column-gap: 20px;
  column-count: 2;
  column-gap: 20px;
  white-space: pre-wrap;
}

这里用到了我最近学到的多栏布局的CSS,prince认识它。然后使用prince转成PDF,打开一看效果灰常不错,而且只有六页了!

注意,CSS里的white-space是必须的,不然不会自动折行,左栏的过长文字会叠加到右栏。

PS: prince共享版会在每个生成的文件的第一页的右上角加个图标,但打印时并没有打印出来。

Category: 未分类 | Tags: shell prince
5
24
2010
8

将du的输出按文件大小排序

du命令的输出结果要么是不人性化的全部以千字节为单位,要么加上-h参数,显示为1K 234M 2G这样易读的数据。可是,我通常想查看那些大文件/目录,或者那些小文件/目录。单单只用sort命令的话,就不得不在脑海转换那些千字节单位的数据了。做为一个Linux用户,电脑能做的我可不想让人脑来做。Google了一下,发现这个帖子提供了一段代码可行,但是输出效果并不理想,于是我略作更改,写出了以下代码:

sdu () {
  du -sk $@ | sort -n | awk '
BEGIN {
  split("K,M,G,T", Units, ",");
  FS="\t";
  OFS="\t";
}
{
  u = 1;
  while ($1 >= 1024) {
    $1 = $1 / 1024;
    u += 1
  }
  $1 = sprintf("%.1f%s", $1, Units[u]);
  sub(/\.0/, "", $1);
  print $0;
}'
}

这段代码使用sort排序原始数据后,再使用awk来转换数字的单位。使用方法为sdu后加要查看大小的文件/目录就可以了。注意我在代码中加了-s参数,如果希望同时查询子目录的话,需要去掉这个参数。

Category: shell | Tags: shell linux
4
23
2010
0

统计自己的Vim脚本行数

一行 zsh 命令搞定:

grep -l "Author.*`whoami`" ~/.vim/**/*[^~](.^@)|while read i; do cat $i; done|cat - ~/.vimrc|sed '/^\s*$/d'|sed '/^\s*"/d'|wc -l

解释一下:

grep -l "Author.*`whoami`"
选取包含匹配正则表达式Author.*`whoami`的文件,其中whoami命令当然就是取得当前用户名啦。如果你的Vim脚本里标明的作者和你的系统的用户名不同,请自行修改之。
~/.vim/**/*[^~]
Vim配置目录下的所有文件,但以~结尾的备份文件除外。
(.^@)
前面的条件还不够哦。文件要是非软链接^@的普通文件.。也不知道只用一个.可不可以。
|while read i
将前面管道中的那些文件名,一行行地读到变量i里面来。
cat $i
把文件$i的内容显示出来。
done|cat - ~/.vimrc
把前面的内容-和vimrc文件连接到一起输出。
sed '/^\s*$/d'
空行不算数。
sed '/^\s*"/d'
以引号开头的行是注释,也不算数。
wc -l
数数总共多少行。

我的结果:

1915

看到这个结果,我自己都大吃一惊。没想到一年多以来,自己写了这么多行配置了啊。

不过可能不准确哦。反正我这里竟然算不算软链接都是这么多。哪位有更好的命令不妨留言呀。

Category: shell | Tags: zsh vim shell

| Theme: Aeros 2.0 by TheBuckmaker.com