colaghost

自己的世界。。。

ubuntu下自动切换声音输出设备

我有一个usb耳机,在ubuntu下驱动也正常,不过有一个蛋疼的地方就是每次插进usb耳机都不会自动切换到usb耳机对应的声音输出设备,次次都要手动去选择对应的,于是想着能不能写一个脚本来实现。
不过貌似关于这方面的中文资料不多,google了很久都没有什么结果,倒是找出一个freebsd上能实现的,就是sysctl,不过貌似ubuntu下实现不了。后来在stackoverflow上问老外才得知用pacmd可以列出各种可用的声音输出设备还有设置默认输出设备等操作神马的。
弄了一下,果真OK了,写随意写了一个脚本来测试了,这个脚本假设系统使用pulseaudio的。
原理也不难,就是检测所有的声音设备,找出当前默认的声音设备,然后把声音设备切换到下一个,重新运行脚本就可以把声音设备切换回原来默认的了。

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
#!/bin/bash
 
declare -i count=`pacmd list-sinks | grep -c index:[[:space:]][[:digit:]]`
declare -i active=`pacmd list-sinks | sed -n -e 's/\*[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p'`
declare -i major=$count-1
declare -i next=0
 
if [ $active -ne $major ] ; then
next=active+1
fi
 
pacmd "set-default-sink ${next}"
 
for app in $(pacmd list-sink-inputs | sed -n -e 's/index:[[:space:]]\([[:digit:]]\)/\1/p');
do
pacmd "move-sink-input $app $next"
done
 
declare -i ndx=0
pacmd list-sinks | sed -n -e 's/device.description[[:space:]]=[[:space:]]"\(.*\)"/\1/p' | while read line;
do
if [ $next -eq $ndx ] ; then
notify-send -i notification-audio-volume-high "声音输出切换到" "$line"
exit
fi
ndx+=1
done;

脚本里在切换后会做notify-send的提示,假设找不到命令的可以执行sudo apt-get install libnotify-bin安装一下,但不安装也没有什么关系,只是一个提示而已。

为了方便可以将脚本复制到/usr/bin目录下,然后在“键盘快捷键”里面添加多一个快捷键,命令就是脚本名了,最后设定好想要的快捷键,以后就可以直接切换了。

ubuntu下看flash视频时禁用屏保

最近两天在啃《unix环境高级编程》,一直转在ubuntu下。我吃饭时喜欢一边看电影,可是上优酷看flash视频时又有一个麻烦的地方,就是屏保并不会自动被禁用掉,而是得手动去禁用,看完又得去把它开启,甚是麻烦,就想着自己写一个脚本来实现开启/禁用屏保。

Google了下,关于这方面的结果倒是不少,下面是其中一个脚本:

1
2
3
4
5
6
7
8
#!/bin/sh
while true;
do
    if test -n "`file /tmp/Flash*|grep \"Macromedia Flash Video\\|MPEG v4\"`";then
        gnome-screensaver-command -p
    fi
    sleep 30
done

可惜我发现我的tmp目录下并不会生成/tmp/Flash*,我原先以为是我用shockwave flash的原因,不是在tmp目录下生成缓存。就自己搜索任何可能的目录,看下具体在哪个目录里生成缓存,可惜找不到。

这时候想到linux下是可以通过lsof来监测某一个进程当前打开了哪些文件,结果发现了其实是有在/tmp目录下生成Flash*这样的文件的,只是生成后马上又被删除了,这时候虽说进程还可以进行读写操作,可是在目录下却无法看到。

因此想到了另外一种方法,由于shockwave flash做为chrome下的一个插件,只要监测到chrome载入libgcflashplayer.so的这一个进程打开的所有文件下有/tmp/Flash*类似的文件即可知道当前在播放flash视频,这时候只要运行”gnome-screensaver-commad -p”模拟活动一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
while true;
do
	pid=`ps aux | grep /opt/google/chrome/libgcflashplayer.so | grep -v grep | awk '{print $2}'`
	if [ "$pid" != "" ]; then
		data=`lsof -p $pid | grep /tmp/Flash`
		if [ "$data" != "" ]; then
			echo "gnome-screensaver-command"
			gnome-screensaver-command -p
		fi
	fi
	sleep 30
done

用expect来实现自动交互的shell脚本

有时候可能实现自动交互过程,如自动登陆到远程主机等,由于需要输入用户名密码神马的,得自己呆在那里等它登陆完成,比较麻烦,单纯用shell脚本又没法完成,因为像ssh连接远程主机是不从stdin读入数据的。这时候就需要expect出马了。

Expect是基于TCL的,作为一个脚本语言,expect能在无需管理员参与的情况下实现自动交互(比如passwd,fsck,telnet等),expect也能用于自动测试一些应用程序。

搜索一下其实网上很多使用expect来完成密码登陆的例子的:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
auto_login_ssh () {
    expect -c "set timeout -1;
                spawn -noecho ssh -o StrictHostKeyChecking=no $2 ${@:3};
                expect *assword:*;
                send -- $1\r;
                interact;";
}
 
auto_login_ssh passwd user@

但实际上这个脚本不能做到完全自动化,一旦密码错误或者远程主机未响应神马的,expect还是正常退出,并不能判断到底哪一步出问题了。由于使用expect后,程序的exit status是expect的,所以这时候对expect做处理。当expect遇不到期望的输出值时,就会执行eof分支,这时候我们只要在eof分支加上退出值即可以判定到底是在哪一步出了问题。

这里用两个简单的脚本说明,这样比较容易弄清楚。

called.sh

1
2
#!/bin/bash
echo "a"

可以看出上述脚本是输出“a”,也就是期望输出值应该为”a”。下面看用expect实现的交互脚本:
caller.sh

1
2
3
4
5
6
7
8
9
#!/bin/sh
expect -c "set timeout -1;
           spawn called.sh;
           expect{
               \"b\"{exit 1;}
               eof{exit 2;}
           }
           "
echo $?

实际上called.sh的输出值是”a”,但caller.sh的期望值为”b”,这时候实际上是跳到eof,这时候按状态2退出,也就是说$?实际上为2.这时候就可以判断交互脚本具体的执行情况。

这里只是一个简单的实例而已,并没有任何实际的意义,但是它说明了如何处理一些异常的情况,这样只要将上面的自动完成ssh连接远程主机的脚本加上异常情况处理即可变成一个真正完全自动交互的脚本了,以下是做修改后的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
auto_smart_ssh () {
    expect -c "set timeout -1;
                spawn ssh -o StrictHostKeyChecking=no $2 ${@:3};
                expect {
                    *assword:* {send -- $1\r;
                                 expect {
                                    *denied* {exit 2;}
                                    eof
                                 }
                    }
                    eof         {exit 1;}
                }
                "
    return $?
}
 
auto_smart_ssh passwd user@host ls /var
echo -e "\n---Exit Status: $?"

注:以上自动完成ssh连接远程主机例子来自于apt-blog.net

统计源码文件行数的小程序

这些天在公司一直在看一个开源包,由于某种原因突然想看下这个开源包到底写了多少行代码。简单的方法:一个个文件查看再利用计算器进行加法运算!但这方法也太笨了点吧,再说,源文件那么多,我又那么懒。

想来想去还是写个脚本吧,以后或许也还有用。

记得wc -l可以直接统计文件的行数,不过输出结果是类似于“86 a.cpp”这样的形式的,还得用cut把后面的文件名给去掉。这样只需把整个文件夹的文件扫描一遍即可得到最后想要的行数了。

不过还有两个问题:一个是文件夹里的子文件夹没被扫描到,因为循环时是判断为普通文件的才进行统计;另一个是由于源文件后缀形式多样,有.java,.c,.c++等等等,必须有参数把它们传进脚本里。

第一个问题好办,不就是个递归吗?可是由于本人对SHELL编程的熟悉程度不够,递归时如何返回值就搞了很久,一直用return却只是获取不到,不知何故!最后直接用一个全局变量解决了这个问题。

第二个问题最开始就想着把每个后缀名作为单独的参数传进去,这样就涉及到参数个数不确定的问题。知道有shift这个东东可以循环扫描参数,但是又不知道如何把它们保存下来。搞到最后还是直接用”c:java”这种形式做为一个参数传进去算了,再用awk进行分解,awk里又涉及到访问外部变量和循环语句等的使用,甚是蛋疼,查了一部分资料。

最终效果是这样的:

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
#! /bin/sh
lines=0
count_line ()
{
	cd $1
	for file in $(ls); do
		if [ -d $file ]; then
			count_line $file $2
		else
			flag=$(echo $2 | awk -v exc=${file##*.} 'BEGIN {FS=":"}{for(i=1; i<=NF; i++){if($i==exc){print 1;break;} if(i==NF)print 0}}')
			if [ $flag -eq 1 ]; then
				lines=$(($lines+$(echo $(wc -l $file) | cut -d ' ' -f 1)))
			fi
		fi
	done
	if [ "$1"!="." ]; then
		cd ..
	fi
}
 
if [ $# -lt 2 ]; then
	echo "Requires the path and the param!"
else
	count_line $1 $2
	echo "All files have $lines lines"
fi