写个 Alfred 插件之“更新 hosts”

最近通过研究 Alfred 的插件,让不会编程的 Lucifr 也开拓了不少的思路,看到了 Shell、Applescript、和 Workflow 的强大之处,萌生了很多可以利用 Alfred 来简化复杂流程的想法,并且竟然都磕磕绊绊的实现了。于是乎难免有些飘飘然,想要分享下自己的喜悦(说白了就是得瑟一下),或许会形成一个《写个 Alfred 插件》系列也不一定(拖延症患者还是不要乱承诺的好)。

和一些大牛们发的插件不同,Lucifr 没那么高的水准,因此只是分享下自己折腾的过程,供参考。

要解决的问题

Lucifr 总是在有 ipv6 的教育网和无 ipv6 的 ipv4 某通网络之间游荡,在 ipv6 环境中可以用到一些 ipv6 hosts 来访问一些不存在的网站或是进行加速,而在 ipv4 网络中则没有这个福利,因此总是要编辑 /etc/hosts 这个文件。我之前的笨方法是把 ipv6 的 hosts 添在最后,切换网络环境时手动添加或删除,然而近万行的 hosts 文件打开都很慢,经常换网络环境就太麻烦了。

解决思路和过程

因为 Lucifr 同时在用 Mac 的“位置”(Location)功能来在不同环境下切换 pac 自动代理文件,所以打算从这上面做文章。让 Alfred 脚本读取当前的位置信息,在相应的位置对 /etc/hosts 做出相应的更改。

很快便锁定了这两个 Shell 命令:networksetup getcurrentlocation 用来获取当前的位置;cat file1 > file2cat file1 file2 > file3 用来更改文件内容。

这样就可以把 ipv4 的 hosts 和 ipv6 的 hosts 分别写在两个文件里,比如 ipv4 和 ipv6,当位置是在家中(home)时,则修改 hosts 内容为 ipv4,而在 ipv6 环境中时,则先添加 ipv4 再添加 ipv6 的内容。如下:

ipv4="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv4"
ipv6="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv6"
LOCATION=$(networksetup getcurrentlocation)
if [ $LOCATION = 'ipv6' ]; then
  sudo sh -c "cat /dev/null > /etc/hosts; cat $ipv4 
$ipv6 > /etc/hosts"
elif [ $LOCATION = 'home' ]; then
  sudo sh -c "cat /dev/null > /etc/hosts; cat $ipv4 > 
/etc/hosts"
fi

但这里又遇到了一个问题:修改 /etc/hosts 是要管理员权限的,要用到 sudoer 和管理员密码,把密码以明文形式写到插件里显然是不安全的。

一番搜索之后,Lucifr 找到了 Dirtdon 制作的 Authenticate for Alfred,这个小程序可以将用户名和密码储存在 OS X 的钥匙串中,使用简单,把 Authenticate.app 放到插件的目录下就可以使用。调用起来也很简单:

USER=$(Authenticate.app/Contents/MacOS/Authenticate -get 
username)
PASS=$(Authenticate.app/Contents/MacOS/Authenticate -get 
password)
if [ $USER='' ] && [ $PASS='' ]; then
  Authenticate.app/Contents/MacOS/Authenticate
else
...
fi

第一次使用的时候它就会弹出一个对话框要求输入用户名和密码,确认后就可以保存在钥匙串里(每个插件一条记录),下次它就会自动调用了。

Authenticate for Alfred

这样代码就变成了这样:

USER=$(Authenticate.app/Contents/MacOS/Authenticate -get 
username)
PASS=$(Authenticate.app/Contents/MacOS/Authenticate -get 
password)
LOCATION=$(networksetup getcurrentlocation)
ipv4="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv4"
ipv6="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv6"
if [ $USER='' ] && [ $PASS='' ]; then
  Authenticate.app/Contents/MacOS/Authenticate
else
  if [ $LOCATION = 'ipv6' ]; then
    echo $PASS | sudo -S sh -c "cat /dev/null > 
/etc/hosts; cat $ipv4 $ipv6 > /etc/hosts"
  elif [ $LOCATION = 'home' ]; then
    echo $PASS | sudo -S sh -c "cat /dev/null > 
/etc/hosts; cat $ipv4 > /etc/hosts"
  fi
fi

最后,建立一个新的 Shell Script 类的插件,把 Authenticate.app 放到插件的目录中,把代码粘进 Command 里就行了:

Host Update

当然,也可以和之前发过的更换网络位置的插件合到一起,这样更改位置的同时就会修改 hosts 了:

MSG=`/usr/sbin/scselect 2>&1 | /usr/bin/egrep -i "\(
{query}" | /usr/bin/cut -c 4- | /usr/bin/cut -f 1 | 
/usr/bin/head -n 1 | /usr/bin/xargs /usr/sbin/scselect | 
sed -E 's/.+ \((.+)\)/Location changed to \1/'`; if [ 
"$MSG" = "" ]; then echo Unable to change location; else 
echo $MSG; fi;
USER=$(Authenticate.app/Contents/MacOS/Authenticate -get 
username)
PASS=$(Authenticate.app/Contents/MacOS/Authenticate -get 
password)
LOCATION=$(networksetup getcurrentlocation)
ipv4="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv4"
ipv6="/Users/$(whoami)/Dropbox/Sync\ Files/hosts/ipv6"
if [ $USER='' ] && [ $PASS='' ]; then
  Authenticate.app/Contents/MacOS/Authenticate
else
  if [ $LOCATION = 'ipv6' ]; then
    echo $PASS | sudo -S sh -c "cat /dev/null > 
/etc/hosts; cat $ipv4 $ipv6 > /etc/hosts"
  elif [ $LOCATION = 'home' ]; then
    echo $PASS | sudo -S sh -c "cat /dev/null > 
/etc/hosts; cat $ipv4 > /etc/hosts"
  fi
fi

凑够三个标题

并不是通用的插件,也就不提供下载了,这种工具果然还是自己造来才最顺手,折腾吧,少年~

Lucifr

Read more posts by this author.

Beijing, China http://lucifr.com