Showing newest 42 of 51 posts from November 2010. Show older posts
Showing newest 42 of 51 posts from November 2010. Show older posts

My weekly Gentoo update process

How often should we update our Gentoo?

There is no absolute correct answer for that. Gentoo is just like other distros2, s**t does happen when do update. No matter what species of penguins you pet, they all might get sick and updating interval is not really relevant. However, you should never wait for a year even just a few months, though your version might still be supported. Believe me, that's not wise. Gentoo is a rolling release, it doesn't imply you must update often or when you must do the update, not like those 6-month update cycle distro. But if you wait up for months, you might encounter many trouble to get your Gentoo updated. I have seen probably a couple of forums posts asking how they can update after years without using the computers, they were all advised to start a freah install of latest version.

Since I started to use eix, eix-diff told me the changes on Portage tree. I used to update daily, but I didn't do that anymore. Because it's very often only a few packages would get updated. I am now using a weekly update method and I have been doing this for more than a month.

Here is the steps I do:

  1. Running sudo eix-sync to update Portage tree, read the diff.
    • See what packages get removed, you might have few removed, see if there are replacements.
    • See what packages get added, play with them.
  2. Running emerge -pvuDt world to see what can be upgraded.
    • -t shows you the dependency, it's very helpful to understand why a package is pulled into update list. Remember only those packages rendered in bold text are in your world file /var/lib/portage/world. world file contains packages which you specifically told emerge to install without using -1 aka --oneshot option.
    • Also pay attention to package list at top level (leftmost at output of emerge -t) which are not rendered in bold text, they belong to the system set packages. As what system sounds, you should try to make them up to date.
    • You might have some blocked packages, if you can't resolve, search on forums.
    • If you are informed there are news using eselect news to read, make you you read them.
  3. It's not necessary to update all packages. I usually update packages has updated on minor version number, but that's just me. Besides that, I read logs using emerge -pvul <package> to see if I really want to update. You should also pay attention on USE flags, some might be removed, added, or switched between default-on/off.
  4. When I have a list to update, I would run emerge -quD1 to update. -1 to make sure you don't put packages into your world file. By the way, I recently found out you can do emerge -pvuD world | sudo genlop -p1 to get a estimated merge time.
  5. After update, make sure you read all messages. You can also find them at /var/log/portage/elog.
    • Do what they tell you, if it tells you to run a program, then run it.
    • If there is configuration files update, then run dispatch-conf to update your configuration files and don't just press u to use new configuration file, you might be overriding what changes you have made.
    • If you want to check last merge time, you can run sudo genlop -tl --date 1 day ago it should give you updates in last one day. Or I have written a short script to check the result of last merge.
  6. Running revdep-build.

There are some packages might require you to update partial or even entire system, meaning all packages. For instance, GCC, but it's not always as I updated from 4.4.3-r2 to 4.4.4-r2. You can rebuild all of course, but be honest, do you really know what improvement has been made in that update and how much improvement of performance you can get by re-compiling with newer version of GCC? Don't waste energy since your system is running well for you.

So, when do you need to do such mass rebuild? When emerge tells you to do so. I only did full system rebuild once when I updated GCC, I think it's when I installed 4.4.3-r2 to new slot. There is another case you might need to do mass rebuild if you change USE flag in /etc/make.conf. But it's still not necessary, you can wait for packages to get new version, then use emerge -pvuDNt <package> to do update, so you won't have to rebuild just for new USE flag. I removed nls a while ago, many packages are not merged with nls, I still have 46 are still with nls. There is one more case, the X window and its drivers. Some didn't read the message and they posted why keyboard, mouse, video didn't work after updated.

Gentoo has some guides about upgrade, e.g. GCC and Kernel. When you have doubts or you are nervous about an update, do a search for those guides. Or wait for a little bit, say a few days, then search on forums to see if there is any victims :) Also bug reports might be helpful, search package on Gentoo Packages, there is a Bugs link, which can lead you to bug list of that package.

Trying not to use unstable packages or hardmask packages, I currently only has one package in /etc/portage/package.keyword. Those packages usually bring other unstable packages in. It's not like they are seriously unstable, but doing so you might need to be more careful. I actually had been living with those for quite a long time, I believe I had ever had probably 50 packages in keywords and a couple in unmask. It's easy to get lots of packages to be updated after Portage tree sync, I was kind of tired of that, therefore I started to remove them from package.keyword. If you like newest stuff, go ahead, just be sure you read all messages. I didn't meet any problems after I successfully merged them. It's more common that you see error messages when merge unstable packages than stable packages, but since they are not merged, you are safe.

Don't forget to subscribe to Gentoo Linux Security Advisories, you can also see them at Latest Site News section on front page of Gentoo Forums. If I recall correctly, they are published only after there is a fix or a new version which is immune to threat is available. When you see them in your installed packages list, do the update.

I also set up a code in Conky, so I would know when to do update or how long it's been overdue:

2010-11-28--09:18:13

The screenshot above used my new code requiring td.sh, here is an old post about my old code.

Lastly, this is just my way to do update, it might not be suitable to you.


  1. genlop is also the package name, which is a Perl script, doing similar job as qlop does. It's more powerful, but personally I am not used to its output format. ^

  2. Yes, I do mean to include binary package management distros. ^

6.4 seconds loading time? Unacceptable!

Google Webmaster Tools told me:

Performance overview

On average, pages in your site take 6.4 seconds to load (updated on Oct 31, 2010). This is slower than 83% of sites. These estimates are of low accuracy (fewer than 100 data points). The chart below shows how your site's average page load time has changed over the last few months. For your reference, it also shows the 20th percentile value across all sites, separating slow and fast load times.

Chart

6.4 seconds?! Are you kidding me? Well, it's not. My posts pages really take about that long to load. You might want to ask me, what's the problem with 6.4 seconds? It's not really long comparing to other blogs. It's not, but if you look at my page, you don't see any crap banners, icons, images. My blog is basically pure text only, except the images in post contents and two Google AdSense ad units. So, it's too long to me.

I am pretty sure where I can cut the number down: Disqus!

2010-11-27--18:07:15

If you scroll down, you will see Disqus is no longer loaded by default. You will need to click on that button to load comments. I also make related posts list loaded by user request, that helps a little, and I moved jQuery code into yjlv.js, request_count--. I know this is cheating in order to get low loading time.

Here is a diff for my template changes:

--- template.xml.orig   2010-11-28 08:11:25.000000000 +0800
+++ template.xml    2010-11-28 07:58:55.000000000 +0800
@@ -3,7 +3,7 @@
 <html xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
   <head>
     <b:if cond='data:blog.pageType == &quot;item&quot;'>
-      <title><data:blog.pageName/> &lt;&lt;&lt; $(<data:blog.title/>)</title>
+      <title><data:blog.pageName/></title>
       <b:else/>
       <b:if cond='data:blog.pageType == &quot;static_page&quot;'>
         <title><data:blog.pageName/> &lt;&lt;&lt; $(<data:blog.title/> --page)</title>
@@ -26,8 +26,8 @@
     <data:blog.feedLinks/>
     <b:skin><![CDATA[]]></b:skin>
     <link href='http://www.yjl.im/css/yjlv.css?4' rel='stylesheet'/>
M#-    <script src='http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js'/>
     <!--[if lt IE 9]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+    <script src="http://www.yjl.im/js/yjlv.js?8"></script>
   </head>
   <body>
     <header id='blog-header'>
@@ -253,7 +253,12 @@
             </div>
             <footer>
               <b:if cond='data:blog.pageType == &quot;item&quot;'>
-                <div style='float:right;width:312px'>        Possibly (Definitely Not) Related Posts:        <div id='gas-results'/>      </div>
+                <div style='float:right;width:312px'>
+                   <div>Possibly (Definitely Not) Related Posts:</div>
+                   <div id='gas-results'>
+                       <input type="button" value="Click to load related posts list" onclick='$.getScript("http://brps.appspot.com/gas.js")'/>
+                   </div>
+               </div>
               </b:if>
               <div class='post-footer-line post-footer-line-1'>
                 <span class='post-author vcard'>
@@ -312,11 +317,19 @@
             </footer>
             <b:if cond='data:blog.pageType == &quot;item&quot;'>
               <section id='post-comments'>
-                <h2>Comments</h2>
+                <h2><a expr:href='data:post.url + &quot;#disqus_thread&quot;'>Comments</a></h2>
                 <div id='disqus_thread'/>
-                <script type='text/javascript'> if (document.location.href.indexOf(&#39;/b/post-preview&#39;) == -1) $.getScript(&#39;http://yjlv.disqus.com/embed.js&#39;);</script>
-                <noscript>Please enable JavaScript to view the <a href='http://disqus.com/?ref_noscript=yjlv'>comments powered by Disqus.</a></noscript>
-                <a class='dsq-brlink' href='http://disqus.com'>blog comments powered by <span class='logo-disqus'>Disqus</span></a>
+               <script>
+               $(function(){
+                   // If visitors are led to comments, then load comments automatically.
+                   var href = document.location.href;
+                   if (href.indexOf('#disqus_thread') >= 0 || href.indexOf('#comment-') >=0) {
+                       $.getScript("http://yjlv.disqus.com/embed.js");
+                       $('#comments-loader-button').remove();
+                       }
+                   });
+               </script>
+               <input type="button" id="comments-loader-button" style="width:620px;margin:10px;" value="Click to load comments or to write a comment" onclick='$.getScript("http://yjlv.disqus.com/embed.js");$(this).remove();'/>
               </section>
             </b:if>
           </article>
@@ -639,4 +652,4 @@
       </div>
     </footer>
   </body>
-</html>
\ No newline at end of file
+</html>

I added a small piece of code which will automatically load comments when visitors come via a link like .../post-title.html#disqus_thread or .../post-title.html#comment-1234567. Visitors will have no problems to read the comment.

Also, I made a change to posts' page titles. I removed <<< $(YJL --verbose) from title because I saw this in my Stats page:

2010-11-27--17:38:09

The last keyword "yjl table" made me do so. That visitor must found nothing what he or she was looking for. "yjl" matched on page title, search engines are just not smart as you and me. If I was that visitor, I would not even click on the result since it's clear that matched part is useless.

We will see if next check (after 2010-11-27T08:47:50-07:00) will resullt a significant drop on loading time, I believe it will.

Using mplayer to play pronunciation MP3 on Google Dictionary in Vimperator

It's not like that I don't have Flash installed. But every time I click on the black speaker icon (and I have to use mouse to click), Flash gains the focus.

So I modified my ~/.vimperatorrc, I was thinking of using HTML5 Audio, unfortunately, MP3 is not supported.

So, I turned to use mplayer via io.system(), it does a great job.

Now, after I click on speaker icon, I can continue to type Vimperator commands. If I want to go back to input box, I type gi. I also bind s key to play first pronunciation, of course, I have to press <ESC> first if the focus is on input box.

I noticed if I leave the page and go back to play, I will see errors. I am not sure what's the problem, I guess I didn't code it exactly right.

My new Labels page

After posted about how to retrieve labels, I was too bored and by unknown miracle, I decided to write this Labels page. Shamelessly to say, I am very happy for I have done.

If you want to have same page, here is how you can make one:

  1. Get the code from source of Labels page. You need to copy from <!-- BEGIN: whole code --> to <!-- END: whole code -->.
  2. You need to modify <!-- BEGIN: settings -->, read the comments and do what they say.
  3. Create a new page and post the code you have modified.
  4. If you need to update the code, check Last updated, if it's different, then paste the new code. Normally, you only need to update main code. You might want to make some changes to style or HTML.

Retrieving labels of a Blogger blog using JavaScript

I want1 to get a list of labels after I read this post, which is about the blogger's index page of his recipes. It's handmade list and it's kind of strange since the code uses jQuery but reload page for updating list of posts. I want to list all labels automatically and update list of posts in place.

(Warning: It's really a long long long long list!)

Please view page source for the source code (BSD License). At this moment, it just literally lists all labels. As you can see, it's not easy to read. I am thinking for a better way. Maybe split them by alphabets, put them into a drop down menu, or have something auto-completion feature in an input box? Of course, my final Labels (index) page will list posts as you seen in that recipes index page. But I will use Blogger API only, that index uses Yahoo! Pipes, another mystery to me.

There is one thing missing, the post counts of a label, the Blogger API doesn't give us that information. For this point, you might be wondering why I don't just parse content of Labels gadget. The reason is the content of Labels gadget is directly visible to search engine crawlers and they are not smart including Googlebot, they would match keywords on labels list. I don't want people to visit my blog because that kind false match, therefore I don't even use Labels gadget.

I am really not sure if I will continue to write this Labels page because honestly I have only clicked on other blogs' label/tag links for a few times. I don't really need to have a Labels page, it certainly is convenient but not really necessary for me to have one. For last 30 days, I got 14 out of 3328 pageviews on label pages, which is 0.434%. Do I really need one of that index page?

Updated on about new Labels page

I wrote that page, you can see it here.


  1. Actually I have been wanting to retrieve a list of labels since I knew about Blogger API, never knew how to do so. But hours ago, I googled for that again. This time I found this reply^

Ctrl+Shift+PageUp to move tab in Firefox using Vimperator

I think I used to press <Ctrl+Shift+PageUp> and <Ctrl+Shift+PageDown> when I started to use tabbing in Gnome Terminal or gedit if I recall correctly.

I think Firefox did support such keys for some time, but I could be wrong. Chromium supports them. If you are a mouse person, you probably think this is not important. But if you also use multi-profile at the same time, i.e. using --no-remote for other profiles. The drag and drop for bookmarks or tabs doesn't work for those browser processes with no-remote, which causes you can not drag a tab handler to re-order tabs, hench you need another way to re-order.

Using Vimperator proves that's a great advantage once again. By adding the following lines to ~/.vimperatorrc:

map <C-S-PageUp> :tabm -1<CR>
map <C-S-PageDown> :tabm +1<CR>

I can use those keys to move a tab.

If you prefer JavaScript version, you can use:

map <C-S-PageUp> :js getBrowser().moveTabBackward()<CR>
map <C-S-PageDown> :js getBrowser().moveTabForward()<CR>

They work less perfect than Vimperator built-in command :tabm[ove] when the tab is the leftmost or rightmost tab and it also steals focus for tab handler.

Monitoring file system changes with inotifywait and Conky

I wanted to know how I could monitor file system changes, especially activities on my home directory, and this was about this thread. I was thinking the issue could be solved by generating a snapshot of files using find, then watching changes and making changes to the snapshot file. Using grep to do the search.

But, that's simply stupid. Just using find would do that OP wants since that program OP uses only search for file names and folder names. For the first run of find it might be slower, 31000 more files took about six seconds. Well it's not really slow. The consecutive runs only take about 0.1 seconds even after you add or delete files.

Anyway, it's still fun to know how to monitor files. I installed inotify-tools package for inotifywait program.

Monitoring file system changes

Here is the script I use:

inotifywait -m -r --format $'%T %e %w%f' --timefmt '%H:%M:%S' --exclude ~/'(\.mozilla|Documents/KeepNote)' -e modify -e move -e create -e delete ~ 2>&1 | awk '/^[0-9]/ {
sub(/'"${HOME//\//\\/}"'/, "~", $0)
split($0, a, " ")
len=length(a[1])+length(a[2])+1
printf "%-20s %s\n", substr($0, 0, len), substr($0, len+2)
// flush stdout
system("")
next
}
{print ; system("")}
' | tee -a /tmp/home_monitor

-m and -r are for recursively monitoring on files. I set up the output format and timestamp format. I exclude two things from being listed, one is ~/.mozilla and another is KeepNote's files. You can only have one --exclude supplied, if you have more than one, then only the last one would be effective. You have to group them up. -e specify events you want to monitor.

I use awk to do format adjustment, column alignment. Also replacing the literal of your home directory, /home/username, with just ~. Then I pipe the output to tee, so I can see on screen and write output to file at the same time. This way, Conky can ${tail /tmp/home_monitor 10} the file.

I was planning to use named pipe:

$ MPIPE=/tmp/home_monitor
$ mkfifo $MPIPE
$ inotifywait ... | awk ... >$MPIPE
$ tail -f $MPIPE

It will save some disk space, but it doesn't work with Conky.

If you want to clean up /tmp/home_monitor when it gets too big, just do echo '' > /tmp/home_monitor. An important note for you, when you tail a file in conky, make sure it exists. If you remove the file, conky exits. I was hoping there was a Conky variable would run program in a thread and do similar task as $tail does, only the input is the stdout of that thread not a file.

By the way, I had never thought even in my own home directory would have many activities.

Automatic inputting quotation marks and move cursor in between in Bash

I write my blog posts with Markdown and I have many files named with post titles. So, every time I am about to write a new post, I do:

$ cd /path/to/
$ touch 'New post.mkd'
$ vi !$

Quotation marks must be used because of spaces, \<Space> is not a good alternate option. I would have to enter two single quote, then move my cursor in between them. I used to type two at once or I often forget to type the closing quote.

I decided to solve my problem. It's a problem because I don't want to move my hand to arrow keys, there probably have keys to move by characters using control key plus something, but I am not aware of. Anyway, I added the following to ~/.inputrc:

"\C-x\'": "'' \e[D\e[D"
"\C-x\"": "\"\" \e[D\e[D"

I was wanting to use <C-'> but it's not possible, it returns '. So I need an prefix and <C-x> seems to be an nice choice. Whenever I need quotes, I type <C-x>' or <C-x>", depends on what type of quotes I would need. Two quotes and one space will be placed, then the cursor will be moved in between quotes using \e[D.

You might be wondering why I append a space, the reason is simple. If I have additional argument need to put after the quotes, I can press <C-Right> (or just <End>) because I also have:

# Screen/tmux
"\eOD": backward-word
"\eOC": forward-word
# xterm/urxvt
"\e[d": backward-word
"\e[c": forward-word

I don't need to move cursor out of quotes and type a space.

Multiprocessing in Bash

This is my first time to do such thing intentionally. I believe everyone who has experience of writing shell script, they all have already done multiprocessing, for example: command &.

Here is an example output of a script I am about to show you:

# Creating 10 test files at /tmp/test{0..9}.bin, each is 10MB
$ for ((i=0;i<10;i++)); do head -c 10m /dev/urandom > "/tmp/test${i}.bin"; done

# The single process
$ time for f in /tmp/test?.bin; do md5sum "$f"; done
a928dc064f3b0f68386ff8e8ae8c3d8e  /tmp/test0.bin
59a2940703258a750a6895efbfead10e  /tmp/test1.bin
77dc3bb2b0d70ada17174f73d9b8ba5b  /tmp/test2.bin
e8be270104dc99d7fc842f6b1a8ed622  /tmp/test3.bin
dedd45d0f8168ed3c9ecbf4e7458ab87  /tmp/test4.bin
efaaa7064a849ab4f4dbd69153fcc11b  /tmp/test5.bin
961520ac959d156a71d80628d001a20b  /tmp/test6.bin
110185133ecc6b538b0c383295f3a137  /tmp/test7.bin
3f1901a68e828c7dfe16f1a84805dedc  /tmp/test8.bin
a4032ebc7417b844fc58a841557c73a4  /tmp/test9.bin

real    0m0.426s
user    0m0.338s
sys     0m0.066s

# Multiprocessing with three processes to work on data
$ time ./mp.sh 
W0: W0 started to work...
W1: W1 started to work...
W2: W2 started to work...
W0: a928dc064f3b0f68386ff8e8ae8c3d8e  /tmp/test0.bin
W1: 59a2940703258a750a6895efbfead10e  /tmp/test1.bin
W2: 77dc3bb2b0d70ada17174f73d9b8ba5b  /tmp/test2.bin
W0: e8be270104dc99d7fc842f6b1a8ed622  /tmp/test3.bin
W1: dedd45d0f8168ed3c9ecbf4e7458ab87  /tmp/test4.bin
W2: efaaa7064a849ab4f4dbd69153fcc11b  /tmp/test5.bin
W0: 961520ac959d156a71d80628d001a20b  /tmp/test6.bin
W1: 110185133ecc6b538b0c383295f3a137  /tmp/test7.bin
W0: a4032ebc7417b844fc58a841557c73a4  /tmp/test9.bin
W2: 3f1901a68e828c7dfe16f1a84805dedc  /tmp/test8.bin

real    0m0.265s
user    0m0.342s
sys     0m0.072s

The script mp.sh:

#!/bin/bash

MAX_WORKERS=3

worker () {
  echo "$1 started to work..."
  while read cmd; do
    # if receives exit, then do some finishing jobs
    [[ "$cmd" == "exit" ]] && break
    md5sum "$cmd"
  done
  }


get_next () {
  (( q_id >= ${#queue} )) && next='' && return 0
  next="${queue[q_id]}"
  ((q_id++))
  return
  }


for ((i=0;i<MAX_WORKERS;i++)); do
  # brings up workers and redirection to mute this :
  # ./mp.sh: line 27: warning: execute_coproc: coproc [22652:W0] still exists
  eval "coproc W$i { worker W$i; }" &>/dev/null
done

queue=($(ls -1 /tmp/test?.bin))
q_id=0

while :; do
  for ((i=0;i<MAX_WORKERS;i++)); do
    w_stdout="W$i[0]"
    w_stdin="W$i[1]"
    read data <&${!w_stdout}
    if [[ ! -z "$data" ]]; then
      echo "W$i: $data"
      get_next
      [[ -z "$next" ]] && break
      echo "$next" >&${!w_stdin}
    fi
  done
  [[ -z "$next" ]] && break
done

# clean up
for ((i=0;i<MAX_WORKERS;i++)); do
  w_stdout="W$i[0]"
  w_stdin="W$i[1]"
  echo "exit" >&${!w_stdin}
  # get the rest of data
  while read data; do
    echo "W$i: $data"
  done <&${!w_stdout}
  w_pid="W${i}_PID"
  wait ${!w_pid}
done

worker() is the data processor, which is fed by main program with item from queue array. The main program checks if a worker returns data, if so, then try to get new item for the worker who just finished the processing.

I use coproc to create a subshell for worker(), you should notice that I use eval because coproc doesn't accept using Parameter Expansion to supply the name of co-process. We need to name them from W0, W1, ..., and so on. You don't want to fix the program with fixed number of workers.

In the main loop, you can see w_stdout and w_stdin, which is indirect expansion1, we need it to get the value of W#[0], where # is a digit. When using coproc, the name of co-process is the key to get co-process' standard input/output and process ID, they are ${NAME[1]}, ${NAME[0]}, and ${NAME_PID}, respectively. You can use read data <&${NAME[0]} ; echo "$data" to get the output of co-process, and echo "blah blah blah" >&${NAME[1]} to feed co-process data.

If you design a better protocol for communicating with workers, it surely can do lots of thing. You can even change command(s) for a worker anytime you need. Currently, it only accept exit, so workers can exit gracefully.


  1. data=123 ; point_to=data ; echo "${!point_to}" echos "123". You uses ${!varname} to do such expansion. ^

Printing human readable time duration using Bash

A few times I need to print out a human readable time, e.g. "3 minutes 5 seconds ago." I am not aware a very common tool to print out such text. So, I usually ended up with writing a short code to do such thing.

I decided to write one in Bash a couple of days ago, few hours ago, I wrote td.sh and td-test.sh.

$ ./td.sh 123
2 minutes 3 seconds
$ source td.sh -s
$ print_td 123
2 minutes 3 seconds

You can run it with seconds as argument, or source it and call print_td.

The test file shows more examples of outputs:

$ ./td-test.sh 
Passed: 0 => "0 seconds"
Passed: 1 => "1 second"
Passed: -1 => "1 second"
Passed: 2 => "2 seconds"
Passed: 60 => "1 minute"
Passed: 61 => "1 minute 1 second"
Passed: 3599 => "59 minutes 59 seconds"
Passed: 3600 => "1 hour"
Passed: 3601 => "1 hour 1 second"
Passed: 3660 => "1 hour 1 minute"
Passed: 3661 => "1 hour 1 minute 1 second"
Passed: 86400 => "1 day"
Passed: 172799 => "1 day 23 hours 59 minutes 59 seconds"
Passed: 259199 => "2 days 23 hours 59 minutes 59 seconds"
Passed: 31622401 => "366 days 1 second"
0 failures of 15 tests.
1868 conversions per second via function calls.
203 conversions per second via script executions.

td.sh would not use months, or years, because they are not clear if you want to know how many days exactly under some circumstances, though you probably wouldn't care. It ignores negative sign, it's irrelevant for time duration. You will have to deal with that on you own. It doesn't show those are zeros except you give it 0.

Anyway, you can put this in your conky:

$ echo "$(./td.sh $(($(date +%s -d'Nov 25 18:00 2010') - $(date +%s)))) utill a yummy turkey on table..."
3 days 11 hours 6 minutes utill a yummy turkey on table...

Temporarily removed Popular Posts

I found some search hits on my blog were actually the false positives. The keywords the visitors used were actually hit on the Popular Post list.

I don't like this, so I remove it. It's the same reason why my blog doesn't have Recent Posts or some other whatsoever the lists they are. I hate when I click on a result and find out that's not what I am looking for at all. I have encountered a few times, that the results were from blogs' blogroll. What can you do about it? Nothing, no one do care. Search engine providers do not care, blog owners do not care. Hits are all they care. (The last sentence is not totally true but close)

I even added <meta content='noindex' name='robots'/> to yearly/monthly archive pages for preventing more such occurrences. Search engines are still too dumb, let's all I could say.

I am thinking to add an AJAX version of Popular Posts list, but I haven't got a nice idea to do so. It's obviously I couldn't get the list from the official list since there is no API available.

If I could, I would put some buttons on. Visitors could decide if they want to load of lists of some things, such as Popular Posts, Recent Posts, Recent Comments, etc.

By the way, while I was removing the gadget, I saw Blogger has used a new good-looking dialog:

2010-11-21--07:18:27

Updated 2010-11-21


I was thinking to do something like this, which uses FeedBurner Awareness API to list popular entries. I have written an working code, here is the diff for my existing script:

diff -r c29f74858f02 src/static/g/fb/fb.js
--- a/src/static/g/fb/fb.js Mon Nov 01 19:45:52 2010 +0800
+++ b/src/static/g/fb/fb.js Sun Nov 21 14:19:49 2010 +0800
@@ -33,6 +33,21 @@
   }
 
 
+function lilbtn_g_fb_get_resyndication_data(feed_url, dates, callback) {
+  if (!feed_url || !callback)
+    return
+  google.setOnLoadCallback(function() {
+    var query = "select * from xml where url='https://feedburner.google.com/api/awareness/1.0/GetResyndicationData?uri=" + encodeURIComponent(feed_url) + (!!dates ? "&dates=" + dates : "") + "'"
+    $.getJSON("http://query.yahooapis.com/v1/public/yql?q=" + encodeURIComponent(query) + "&format=json&callback=?", function(json) {
+      if (json.error)
+        callback(json)
+      else
+        callback(json.query.results.rsp);
+      });
+    })
+  }
+
+
 function lilbtn_g_fb_text_render(feed_url, container, _texts) {
   lilbtn_g_fb_get_feed_data(feed_url, function(rsp) {
     if (container == undefined)
@@ -89,4 +104,46 @@
       ele.attr('class', ele.attr('class') + ' lilbtn_g_fb_feedcount');
     });
   }
+
+
+function lilbtn_g_fb_popular_items_render(feed_url, container) {
+  lilbtn_g_fb_get_resyndication_data(feed_url, '2010-11-11,2010-11-19', function(rsp) {
+    if (container == undefined)
+      container = 'lilbtn_g_fb_popular_items';
+    var $ele = $('#' + container);
+    if (rsp.error || rsp.stat != 'ok') {
+      var msg = 'Error on retrieving resyndication data: ' + ((rsp.error) ? rsp.error.description : rsp.err.msg) + '; Click to check out lil\u2218btn website for help';
+      ele.html('<a href="http://lilbtn.appspot.com/help" class="lilbtn_g_fb_popular_items lilbtn_error""><img src="http://lilbtn.appspot.com/img/error.png" alt="' + msg + '" title="' + msg + '"/></a>');
+      return;
+      }
+    var items = {};
+    $.each(rsp.feed.entry, function(idx_e, entry) {
+      if(entry.item)
+      $.each(entry.item, function(idx_i, item) {
+        if (items[item.url]) {
+          items[item.url].score += parseInt(item.itemviews);
+          }
+        else {
+          items[item.url] = {title: item.title, url: item.url, score: parseInt(item.itemviews)};
+          }
+        });
+      });
+    var items_sort = [];
+    $.each(items, function(url, item) {
+      items_sort.push([item.score, item]);
+      });
+    // Sort from highest to lowest score
+    items_sort.sort(function(a,b){return b[0] - a[0];});
+    items = $.map(items_sort, function(item) {return item[1]});
+    delete items_sort;
+    var $ul = $('<ul/>');
+    $.each(items, function(idx, item) {
+      var $li = $('<li/>');
+      var $a = $('<a/>').attr('href', item.url).text(item.score + ': ' + item.title);
+      $li.append($a);
+      $ul.append($li);
+      });
+    $ele.append($ul);
+    });
+  }
 // vim:ts=2:sw=2:et:ai:

I am not going to put this on. After I saw the results, I knew it's not a good idea. Because only entries currently in feed would get view counts or click-through, which doesn't reflect the real view counts. You can not really call them as popular posts. Popularly recent posts might make more sense.

I guess Popular Posts wouldn't come back anytime soon.

Writing data to disk from Vimperator

After I made an automatic downloading for new subscription videos, I recently had this idea: Why I don't take an advantage of it? I could put a video ID into queue directory, then that script will download for me.

It's quite simple to write a file.

:js io.system('echo "Hello World from Vimperator!" \> ~/hi');

That's it. There is no trick or secret, it's as simple as you read. The only thing is you need to escape the redirection >.

I map <S-q> to write a file:

map <S-q> :js (function(){var href=getBrowser().contentDocument.location.href; if(href.indexOf("youtube.com")>=0) {var m=/v=([-_a-zA-Z0-9]+)/.exec(href);if(m){io.system('echo "'+href+'" \> ~/Videos/livibetter/queue/'+m[1]); commandline.echo(m[1]+" queued.");}};})()

Breaking down the function content:

var href = getBrowser().contentDocument.location.href;
if (href.indexOf("youtube.com") >= 0) {
  var m = /v=([-_a-zA-Z0-9]+)/.exec(href);
  if (m) {
    io.system('echo "' + href + '" \\> ~/Videos/livibetter/queue/' + m[1]);
    commandline.echo(m[1] + " queued.")};
    }
  }

Here is the actually code I put into my ~/.vimperatorrc.

The code I use probably only works for UNIX-like system. In fact, io.File should be the more proper way:

var f = new io.File('~/testfile');
f.write("Hey!"); // Works here, but might not what you want! Read next section for correct usage
delete f;

I am not sure if it's okay to close the file using delete, but it works anyway and Vimperator told me there is no close() method. I think this io.File is not exactly the File object proposal by Mozilla, but :echo io.File === File returns true.

Update on File.write()

I was trying to is File.write() to write some data, but the written file only get the last content by write(). I read the source, here is the definition:

/**
 * Writes the string <b>buf</b> to this file.
 *
 * @param {string} buf The file content.
 * @param {string|number} mode The file access mode, a bitwise OR of
 *     the following flags:
 *       {@link #MODE_RDONLY}:   0x01
 *       {@link #MODE_WRONLY}:   0x02
 *       {@link #MODE_RDWR}:     0x04
 *       {@link #MODE_CREATE}:   0x08
 *       {@link #MODE_APPEND}:   0x10
 *       {@link #MODE_TRUNCATE}: 0x20
 *       {@link #MODE_SYNC}:     0x40
 *     Alternatively, the following abbreviations may be used:
 *       ">"  is equivalent to {@link #MODE_WRONLY} | {@link #MODE_CREATE} | {@link #MODE_TRUNCATE}
 *       ">>" is equivalent to {@link #MODE_WRONLY} | {@link #MODE_CREATE} | {@link #MODE_APPEND}
 * @default ">"
 * @param {number} perms The file mode bits of the created file. This
 *     is only used when creating a new file and does not change
 *     permissions if the file exists.
 * @default 0644
 * @param {string} encoding The encoding to used to write the file.
 * @default options["fileencoding"]
 */
write: function (buf, mode, perms, encoding) {

The code from previous section should be:

f.write("Hey!", ">>");
// or `File.MODE_WRONLY | File.MODE_CREATE | File.MODE_APPEND`,
// or just `">"` if it's one-time write on the file

Fast navigation in between pages in Vimperator

Since I started to get familiar with Vimperator, <C-a> is always my best friend when I read a thread on discussion forums. It increases the last number in URL, meaning if a page URL like http://example.com/foo/bar/something?page=123 and you press <C-a>, then you will be brought to page 124. You can also use <C-x> to have opposite direction, it brings you to previous page.

I was reading Buffer, I wanted to learn more about it. One thing caught my eyes, actually more than one. I learned gf can switch between rendered page and page source, I used to right click on page and select View Page Source or <C-z><C-u>. But [[ and ]] are the real things amazing me. They search links with special labels, e.g. next and previous links. It's very useful when you are trying to go through Google search results, they don't use page number. Who knows how much time I had wasted on using mouse to click on next or using f and number to go to next page.

Last merge time

I recently found out I could use qlop -gH package or qlop -tH package to get merge time:

2010-11-20--06:10:42

But it doesn't have an option to list packages merged in a session. So I wrote one to parse /var/log/emerge.log on its own, last-merge-time.sh:

2010-11-20--06:13:43

I tried to mimic the result format.

The merge time calculation is different than qlop, you might see difference in a few seconds. The script uses sed to filter unwanted merge log and keeps the last merge, then uses awk to format the output. You probably noticed that interrupted in the screenshot1 above, it's a result of user interruption (pressing Ctrl+C) while merging. The timestamp is the start time, not the end time of merging as shown by qlop, I am just too lazy to change my code.


  1. The screenshot shows the result by feeding the script with hand-modified raw log. ^

Auto-pinning tabs in Firefox using Vimperator

Firefox 4 introduces App Tab, similar to Pin Tab in Chromium. It's actually Pin tab since you use "Pin as App Tab" to make a tab as App Tab, and it does look like Pin Tab as in Chromium, the tab handler shrinks down to icon only and the tab is moved to the left-most side after already pinned tabs.

It's easy to write a quick command, so certain websites can be pinned automatically. The following code is for pinning a music website:

" Auto-pin
autocmd PageLoad listen.grooveshark.com js if(!getBrowser().mCurrentTab.pinned) getBrowser().pinTab(getBrowser().mCurrentTab);

I don't think it's necessary to check if mCurrentTab is pinned, but that's what I wrote firstly.

You can also write a more general function and match all URLs, so if a page which doesn't need to pin is loaded but the tab is current pinned, you can unpin it using getBrowser().unpinTab().

Better :bmark in Vimperator: Auto-tagging, title clean-up, etc

If you use bookmarks to organize your to-read list, or you bookmark on certain website a lot. You might want to tag with readlater or remove some words from bookmark titles, such as website's name.

For instance, you bookmark questions you want to read or to answer later on Stack Overflow. Normally, the title would be Question blah blah blah - Stack Overflow. If you bookmark very often, you might want to get rid of " - Stack Overflow" and probably tag with "Stack Overflow", so it will be easy to find since :bmark doesn't allow you to put a bookmark in a specific folder.

Here is the final example code, you can put it into your ~/.vimperatorrc:

:js << EOF
function my_bookmark_adder() {
  // stolen from Vimperator source code:
  // http://code.google.com/p/vimperator-labs/source/browse/common/content/bookmarks.js
  let options = {};

  let bmarks = bookmarks.get(buffer.URL).filter(function (bmark) bmark.url == buffer.URL);

  if (bmarks.length == 1)   {
    let bmark = bmarks[0];

    options["-title"] = bmark.title;
    if (bmark.keyword)
      options["-keyword"] = bmark.keyword;
    if (bmark.tags.length > 0)
      options["-tags"] = bmark.tags.join(", ");
    }
  else {
    if (buffer.title != buffer.URL)
      options["-title"] = buffer.title;
    }

  // Doing some stuff you need
  if (buffer.URL.indexOf('http://stackoverflow.com') >= 0) {
    let d = new Date();
    let timetag = '[' + d.getFullYear() + '-' + d.getMonth() + '-' + (d.getDate() < 10 ? '0' : '') + d.getDate() + ']'
    options["-title"] = timetag + ' ' + options["-title"].replace(' - Stack Overflow', '');

    let mytags = ['StackOverflow']
    if (getBrowser().contentDocument.body.innerHTML.indexOf('vote-accepted-on') >= 0)
      mytags.push('Answered');

    if (options["-tags"])
      options["-tags"] = options["-tags"] + ", " + mytags.join(", ");
    else
      options["-tags"] = mytags.join(", ");
    }

  commandline.open(":",
    commands.commandToString({ command: "bmark", options: options, arguments: [buffer.URL], bang: bmarks.length == 1 }),
    modes.EX);
  }
EOF

map a :js my_bookmark_adder()<CR>

The most of the code was copied from Vimperator source, the only code I added is Doing some stuff you need part and the last map command. (You don't need to replace the a key, you can bind it with a new key.)

This example shows how to remove " - Stack Overflow" from title and prefix a "[YYYY-MM-DD]" timestamp, then tag with "Stack Overflow". It also checks if this question is answered, if so, then tag with "Answered".

This is just a simple example to let you know you can have more fun when bookmarking and it's not something Firefox can give you, only Vimperator can.

I believe some people could write a flexible and smart code to remove arbitrary website's name from title, feel free to show off if you just happened to write one.

Customizing Vimperator based on Firefox profiles

If you are more than a usual Firefox user, you probably have been using more than one profile1. For quite a while, I use different userChrome.css to distinguish which profile I was using using different tab bar color. That's very inconvenient because I have to maintain multiple userChrome.css files, each only has small difference in color code.

There is also another issue I wanted to resolve, some stuff I made in Vimperator for one profile, I didn't need those in another profile. But since there is only one ~/.vimperatorrc, it's loaded in all profiles.

The key is to use liberator.profileName to identify profiles. I have made few changes to my ~/.vimperatorrc.

A quick code snippet:

:js << EOF
switch (liberator.profileName) {
  case 'Profile A Name':
    // Do something
    break;
  case 'Profile B Name':
    // Do something else
    break;
  }
EOF

You probably run some Vimperator commands in that file, such as map, autocmd, etc. You now need to execute those commands in JavaScript, for example:

// Get my damn focus back
liberator.execute('autocmd PageLoad (www\\\\.youtube|groups.google)\\\\.com :normal <Esc>');

Basically, you can feed liberator.execute() for whatever command you type in command line of Vimperator. This will get what you used to do, note that those backward slashes in regular expression, you will have to double the amount of those.

I use the following code to change the Vimperator command line bar into red:

// Make command line red
for ([k, v] in Iterator(highlight)) {
  highlight.set(k, highlight.get(k).value.replace('background: #333', 'background: #633'));
  }

It probably is not the most efficient way to do the job, I just skimmed the source code of Vimperator and came out with that after few tries.


  1. If you have no idea what Firefox profile is, try to run firefox -ProfileManager. Not sure about the equivalent command on Windows. ^

Listing inherited Portage profiles and system set packages

I was trying to figure out what packages are part of system set. So I went to check /etc/make.profile and followed any parent profiles for the list of packages in packages file of profiles which make.profile inherits. Soon, I realized that it's easier to use a script, a Bash function actually.

lsprofile() {
  local d l PWD
  cd "${1:-/etc/make.profile}"
  d="$(pwd -P)"
  l=${2:-0}
  echo -e "\e[$((l+1))G$l ${d#/usr/portage/profiles/}"
  [[ ! -f "$d/parent" ]] && return 0

  while read p; do
    lsprofile "$d/$p" $((l+1))
  done < "$d/parent"
}

You can run it without argument or give it one profile file. Here is the result on the profile I used:

$ lsprofile
0 default/linux/amd64/10.0/no-multilib
 1 default/linux/amd64/10.0
  2 default/linux/amd64
   3 base
   3 default/linux
   3 arch/amd64
    4 arch/base
    4 features/multilib
  2 releases/10.0
   3 releases
 1 arch/amd64/no-multilib
  2 arch/amd64
   3 arch/base
   3 features/multilib
 1 features/64bit-native

The first one is the profile of my system. Bigger number means longer distance ancient. You might also want to consult the manpage of Portage and emerge, also Cascading/Stackable Profiles.I then wrote another command to list packages in packages file in those profiles:

lsprofile /etc/make.profile | while read dummy pkg; do grep '^[^#]' "/usr/portage/profiles/$pkg/packages" 2>/dev/null ; done | sort

I think there must be a easy way to list. I did find eix --system --only-names would list, but there are some differences, those1 seem to have been removed from portage tree. emerge -pv system is another way to list.


  1. They all are category virtual package. ^

Auto-downloading YouTube new subscription videos

I have a new stuff showing in my conky:

Auto-downloading new subscription videos

It's a status of downloads.

The script, autoytdl.sh, retrieve the new videos feed via YouTube API and check if there are new entries.

I use cron to run it every hour:

0 * *  * * autodlyt.sh livibetter /home/livibetter/Videos/ 1> /dev/null

The first argument is the account name, second is where to store data and videos. In my case, the downloaded videos go to ~/Videos/livibetter/videos.

If you run it for first time, it will download all videos listed in the feed. It's better to run it and interrupt it, then clean USERNAME/queue, that directory record what videos should be downloaded. It uses youtube-dl to download, one video at a time. So, the downloading process may take very long. If your cron run the script again when previous job isn't finished1, well, same video could be downloaded twice.

This script could be very buggy.

(Normally, I wouldn't put such script into public. But YouTube homepage's Subscription section sometimes showed me videos I had watched through same section again, and YouTube often put be into 360p, or 480p if it thought my Internet speed is fast enough or I was just lucky. 720p, I always have to manually select.)


  1. I am not sure if cron will prevent this or not. ^

manpage to really plain text file

From man man:
man foo | col -b > foo.mantxt
It doesn't work for me, this what I got:
1mNAME0m
       bash - GNU Bourne-Again SHell

1mSYNOPSIS0m
       1mbash 22m[options] [file]

\033[ were removed but the rest of escape code still there. I am not sure why those escape code were modified by col. Anyway, col is obvious not what I need.

I don't want escape code to emphasize text. I want a really plain text output. I tried to read more about man, groff, whatever in man's manpage... I wasn't sure how a manpage being generated.

The easiest workaround is to remove escape code by myself:
man bash | sed $'s/\033\[[^m]*m//g' > bash.txt
(It's still some non-printing chars in the output, run with cat -v)

I still need: setting text width of output or no wrapping.

Updated:  This would do ( echo ".ll 11.5i"; echo ".nr LL 11.5i"; echo ".pl 1100i"; /bin/bzip2 -c -d "/usr/share/man/man1/bash.1.bz2"; echo ".\\\""; echo ".pl \n(nlu+10" ) | /usr/bin/gtbl | /usr/bin/nroff -mandoc | sed $'s/\033\[[^m]*m//g' > bash.txt
. The numbers control the width, but I don't have any idea how to calculator them from text width I need.

Don't be famous!

Or you will be googled for anything about you:

Google is more and more scary

I recently found out you can find out not only the weather and time. Age, eye color, hair color is also possible to get on Google.

Someday we might be able to type this "barack obama whole life."

Updated: I tried two more...

2010-11-12--22:18:39

2010-11-12--22:18:52

and

2010-11-12--22:22:14

also tried children, daughter, dog, those didn't work.

Python as calculator

When I started to use Linux, GCalTool is my first calculator. SpeedCrunch should be next, then HC. The latter twos are nice. I really like SpeedCrunch, unfortunately it's QT1 and GUI.

A while later after I switched to HC, I realized that I already have one perfect calculator installed, Python. I didn't really need graphing, but Python can do that, too.

If you only need +-*/, then you simply run python. If need more math funtions. Here is so far I am doing.

First create ~/.pycalc.py with:

from math import *

Invoking python with PYTHONSTARTUP=~/.pycalc.py python. PYTHONSTARTUP is an environment variable which you can assign a file, Python will try to execute that file before it gives you the interpreter.

I use FlueBox and rxvt-unicode, so I have the following lines in ~/.fluxbox/keys:

# XF86Calculator
148 :Exec urxvtc -e bash -c 'PYTHONSTARTUP=~/.pycalc.py exec python'

Right now, ~.pycalc.py fits all my needs, I don't need any helper functions or something else. Actually, I hardly press that calculator button (XF86Calculator) on my keyboard. I just grab that calculator on my desktop. The most advanced button I have pressed on that basic calculator is %. ? never got a chance to press.

I believe many language interpreters could do the same. If you have a favorite language, try to use their interpreter, it could be the best calculator for you.


  1. I am not against QT, just don't want to have many libraries on my system. The only QT program on my system is SMPlayer. I really hope someday it would support GTK. ^

F1, just another one

"F1" just showed up in my feed reader, it's really nothing new. There are many others for the same purpose, I believe.

For any kind of share button, they are just bad, but I don't deny I have put them on my stuff before. Some give you a set of buttons, some give one button with menu to choose. Anyway, they are still bad.

They ruin your layout and design. Even you design them to match, what's the rate of clicks versus visits? What's the rate of clicks on your buttons versus clicks on buttons in browser's addons or bookmarklets?

If I want to share a link I use bookmarklet provided by the service, I would not want to click on buttons on the page.

If a visitor uses certain of service, they most likely to have an addon installed because many webpages don't even have a Twitter button.

On this blog, I don't have any buttons provided by me. But the Blogger navbar at top does provide Twitter/Facebook/Buzz/Google Reader. My posts got tweeted few times, bookmarked on delicious, and stumbled upon. I would bet those tweets were not via the button in navbar. If they like your stuff, they will share in a way they already know.

So, don't waste time to uglify your pages with those buttons, remove them and make your pages cleaner.

vidir - edit a directory in your text editor

vidir is a Perl script from moreutils.

I didn't know what this script could do at first, I thought it's just numbered files and directories until I read its manpage:

When editing a directory, each item in the directory will appear on its own numbered line. These numbers are how vidir keeps track of what items are changed. Delete lines to remove files from the directory, or edit filenames to rename files. You can also switch pairs of numbers to swap filenames.

Bang! I was shocked. I had never thought of this way to rename files, to delete files, or to switch file names.

At first, I couldn't think of any good example, then I recalled a question on Gentoo Forums, Recursively rename files and folders in Bash, which I had contributed a solution:

_rren() {
[[ "$1" == "" ]] && return 1
for fn in "$1"/*; do
   _fn="${fn// /_}"
   [[ "$fn" != "$_fn" ]] && mv "$fn" "$_fn" && fn="$_fn"
   [[ -d "$fn" ]] && ( _rren "$fn" )
done
true
}

_rren .

The OP wants to rename all files and directories, replace spaces with underlines. It has to be able to do it recursively.

With vidir:

$ find | sort | tail -n +2
./dir a b
./dir a b/e f
./dirc
./dirc/e f
./file a b
./file c d

$ find | sort | tail -n +2 | vidir -

In vi `:%s/ /_/g`, then save and quit

$ find | sort | tail -n +2
./dir_a_b
./dir_a_b/e_f
./dirc
./dirc/e_f
./file_a_b
./file_c_d

Simple and safe because you can review the result. For another example, if you want to delete all *.png:

  1. find -type f.
  2. You search those files first: /.*\.png$.
  3. Check all highlights if they all are needed to be deleted; if not, do remove manually.
  4. Remove contents of those lines: :%s/.*\.png$//.

Simply brilliant!

No such /bin/sh

For a long time, since I started to use Linux to be precise, I always thought I had a true Bourne shell installed until I did:

$ type sh
sh is /bin/sh
$ file /bin/sh
/bin/sh: symbolic link to `bash'

(actually, I did ls -l /bin/sh in second command.)

The truth is almost all of us do not have a Bourne shell. We are using a mimic by Bash (or Dash, if you use Ubuntu or Debian):

If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well. -- man bash

So, I asked "Where the hell Bourne shell is?" to myself. After some searches, I found "The Traditional Bourne Shell Family." I downloaded two sources:

I tried some scripts on my system but I really couldn't find one would fail on two shells above. But, I wasn't really run those script since I didn't want to have unexpected files deletion or something like that.

So, I tried to focus finding side-effect when running Bash as sh. I read one clearly written in Bash manpage:

Brace expansion introduces a slight incompatibility with historical versions of sh. sh does not treat opening or closing braces specially when they appear as part of a word, and preserves them in the output. Bash removes braces from words as a consequence of brace expansion. For example, a word entered to sh as file{1,2} appears identically in the output. The same word is output as file1 file2 after expansion by bash. If strict compatibility with sh is desired, start bash with the +B option or disable brace expansion with the +B option to the set command (see SHELL BUILTIN COMMANDS below).

I tested:

$ sh -c 'echo file{1,2}' # Running bash as sh
file1 file2
$ ./sh -c 'echo file{1,2}' # Heirloom
file{1,2}
$ sh +B -c 'echo file{1,2}' # Turning off brace expansion
file{1,2}
$ dash  -c 'echo file{1,2}' # Dash
file{1,2}
$ ./v7sh  -c 'echo file{1,2}' # V7 shell
file{1,2}

Let's see what would happen if we intend to feed Bash sh with Bash syntax code:

$ bash -c 'for((i=0;i<3;i++)); do echo -n $i; done; echo'
012
$ sh -c 'for((i=0;i<3;i++)); do echo -n $i; done; echo'
012
$ ./sh -c 'for((i=0;i<3;i++)); do echo -n $i; done; echo'
./sh: syntax error at line 1: `(' unexpected
$ ./v7sh  -c 'for((i=0;i<3;i++)); do echo -n $i; done; echo'
./v7sh: syntax error at line 1: `(' unexpected
$ dash  -c 'for((i=0;i<3;i++)); do echo -n $i; done; echo'
dash: Syntax error: Bad for loop variable

No problem at all, it accepts and runs it happily and the result is correct (or incorrect?).

Let's see the memory consumptions:

  PID USER     PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
19166 livibett  20   0 19492  2024  1552 S  0.0  0.1  0:00.03  |   |   `- bash
18890 livibett  20   0 19500  1800  1376 S  0.0  0.1  0:00.00  |   |   `- sh
18838 livibett  20   0  5632   612   508 S  0.0  0.0  0:00.00  |       `- ./sh
26010 livibett  20   0  3980   564   476 S  0.0  0.0  0:00.00  |   |   `- dash  
  726 livibett  20   0  3776   400   312 S  0.0  0.0  0:00.00  |   |   `- ./v7sh

You really save not much when running Bash as sh.

File size:

884984 2010-10-10 07:34 /bin/bash
105592 2010-11-10 21:48 /bin/dash
137583 2010-11-10 21:03 sh # Heirloom
 37112 2010-11-10 22:19 sh # V7

V7 shell is creepily small and Bash is a giant.

Note: I didn't touch compiling process for Heirloom, V7. The rest were compiled using -march=core2 -O2 -pipe -fomit-frame-pointer with emerge.

Would I install Dash, Heirloom shell, or V7 shell as my /bin/sh? The answer is no.

Really

I love this ad:


It's time for a phone to save us from our phones.

But I don't understand how could you not be trapped in this phone again? You still need to use it and you still get caught up.

I always hate phones, not just mobiles. They are so annoying, well, that's what they are for. They have tiny screen, tiny buttons or keyboard. It doesn't matter if it's real buttons or on-screen keyboard, they are just too tiny to type. It doesn't matter if the system is smart enough to correct your typo or any mistakes, because there is no such smart system. If there is, you don't need a phone.

I can't understand why so many people are willing to type or finger scrolling that awkward thing, and think "This is so cool! I could do XYZ on my phone!" Alright, it's cool but it's waste of time. When it's not a time waster, you don't need a phone.

Someday in the future, an ads would be with such tag line: "It's time for not using a phone to save us from our phones."

It does not mean you don't use phone anymore, it means you do not need a phone to do things you used to do on your phone including phoning someone. You do not need a phone to phone. That's really the saver, not having another phone. Another phone, it's just another iteration of an endless loop. You would never be able to get out.

For now, I would rather carrying a 15.4-inch, 6+ lbs laptop with me other than using a light and tiny so-called {smart,i,[insert here yours]}phones.

Please let phone be just phone! You know pressing numbers and hit the call button. That's all you need for a phone.

Remember history often repeats itself! We don't need recreation, we need innovation deep from the foundation.

YouTube, how about an intro clip feature?

I don't have a brilliant brain like Einstein's, but I think this idea is great, an intro clip.

Like TV shows, many YouTube publishers put an intro clip in every video they make. I just watched an intro clip of 30+ seconds of a ~6 minutes video. That video was a recommendation and I had watched same intro before, didn't subscribe to that channel.

That's about 8% of the video size and it's a significant waste in current development of Internet. It's significant because they can reduce it easily.

Such thing has already existed, but in reverse direction, the video ads is an example. Some video streaming websites show you an ad before the video you request, you have to watch it.

If YouTube adds a new field, which allows publisher to choose one as intro clip. The player will play intro clip first, then the main video. Viewer can decide if they want to skip intro.

Of course, there are some new settings. You can always skip intros, or always show intros, etc. Even you decide to show intros every time, as long as the intro clip in cache, you don't need to waste more bandwidth to transfer same content again and again.

It's not all about bandwidth, I just simply want to get into the point, mostly.

But, as usual, wgasa to my idea?

pees on standard input

pee is a C code from moreutils. You can do something with it like

$ pee bc bc
1+2
1+2
[Press Ctrl+D]
3
3

$ echo '1+2' | pee bc bc
3
3

pee accepts arguments as commands. It will feed those commands with the standard input it gets.

As usual for moreutils post, I wrote a Bash script, pees:

#!/bin/bash

if (($# == 0)); then
  echo "Please specify command(s) as argument(s) of $(basename "$0")."
  exit 1
fi

TMPFILE="$(mktemp)"

while read line; do echo "$line" >> "$TMPFILE"; done

for cmdargs in "$@"; do bash -c "$cmdargs" < "$TMPFILE"; done

rm "$TMPFILE"

Needless to say, pees can do the examples above. But it can also do these as pee can:

$ echo 1289371054 | awk '{print strftime("%c", $0, 1)}' > /tmp/tmpdate ; ./pees 'date -f /tmp/tmp
date' 'date -f /tmp/tmpdate -u' <<< ""
Wed Nov 10 14:37:34 CST 2010
Wed Nov 10 06:37:34 UTC 2010

$ date -u > /tmp/tmpdate ; ./pees 'date -f /tmp/tmpdate' 'date -f /tmp/tmpdate -u' <<< ""
Wed Nov 10 14:49:46 CST 2010
Wed Nov 10 06:49:46 UTC 2010

First one is using a specific timestamp, then second is current timestamp. Since date doesn't accept standard input, we have feed it with timestamp in file. pees receives empty standard input via Here Strings. As you can see, two date have different arugments, one with -u, the other without.

There are slightly differences between pee and pees, though both feed commands with data at once, but pee invokes commands before getting data. Therefore, if a command output something before it starts to receive from standard input, you will see the output first if you type in. For example:

$ ./pee 'echo Hello ; while read line; do echo "$line"; done'
Hello
abc
[Press Ctrl+D]
abc

$ ./pees 'echo Hello ; while read line; do echo "$line"; done'
abc
[Press Ctrl+D]
Hello
abc

ts - timestamp standard input (or output and error of a command)

ts is a Perl script from moreutils. You use it with pipe, it receives stdout of another program. The basic idea is doing:

command | ts #

It's useful when you need to know when a program processes something. But it's not enough for me, I wrote a Bash script ts.sh to do more.

#!/bin/bash

DATECMD='date +%H:%M:%S'

process_stdout() {
  while read line; do
    echo -e "\e[44m$($DATECMD)\e[0m $line"
  done
}

process_stderr() {
  while read line; do
    echo -e "\e[41m$($DATECMD)\e[0m $line" 1>&2
  done
}

if (( $# == 0 )); then
  process_stdout
else
  exec 3>&1
  ( bash -c "$*" | process_stdout ) 2>&1 1>&3 | process_stderr
  exec 3>&-
fi

It can timestamp stdout and stderr of a command with different color. See the following example:

ts.sh

The timestamps on red is the stderr of emerge, the rest on blue is the stdout. This script could also be used with pipe, but it would only timestamp on stdin, which is a command's stdout via pipe. It's best to use it like:

ts.sh command arg1 arg2 ... #

An example with shell script as command:

ts.sh while true \; do echo tik tok \; sleep 1 \; done

xlock without PAM

I have made my Gentoo working without PAM. I didn't test xlock before, but then I found I couldn't log in with my password. So I changed to vc1 and killed xlock.

After looked into xlockmore (xlock's package name), I understood I needed to enable xlockrc use flag.

Ran it from command line, I was prompted for entering key (the password):

$ xlock -mode blank
  Key: 
Again: 
$ cat .xlockrc 
Something

Now session lock is back.

chronic - runs a command quietly unless it fails

chronic is a Perl script from moreutils collection, runs a command quietly. It eats up the output of the command, but if the command exits with error, which is exit status does not equal to zero, then chronic would show you the output.

From its manpage, it provides a clear example how it could be useful with cron:

0 1 * * * chronic backup # instead of backup >/dev/null 2>&1

(I use vixin cron, I don't need to redirect stderr to stdout, just chronic backup >/dev/null would do in the old way)

The old way, using redirection is fine, would not give you message sent to stdout since they are redirected to /dev/null, thought you would see get stderr message from log or email if you have sent up.

chronic would get you both.

I made a simple Bash script, chronic.sh:

#!/bin/bash

tmpfile="$(mktemp)"
tmpcode="$(mktemp)"

run_cmd() {
  sh -c "$*"
  echo "$?" > "$tmpcode"
}

exec 3>&1
( run_cmd "$@" | while read line ; do echo "1 $line" >> "$tmpfile" ; done ) 2>&1 1>&3 | while read line ; do echo "2 $line" >> "$tmpfile" ; done
exec 3>&-

exitcode=$(cat "$tmpcode")
((exitcode > 0)) && awk '
/^1 / {print substr($0, 3)}
/^2 / {print substr($0, 3) > "/dev/stderr"}
' < "$tmpfile"

rm "$tmpfile"
rm "$tmpcode"
exit $exitcode

It stores messages into temporary file, also exit code to another temporary file. If command fails, it prints out stdout and stderr.

$ ./chronic.sh "echo 'error' 1>&2 ; echo 'normal' ; grep sdfk sdk" 2>/dev/null ; echo $?
normal
2
$ ./chronic.sh "echo 'error' 1>&2 ; echo 'normal' ; grep sdfk sdk" 1>/dev/null ; echo $?
error
grep: sdk: No such file or directory
2
$ ./chronic.sh "echo 'error' 1>&2 ; echo 'normal' ; grep sdfk sdk" ; echo $?
error
normal
grep: sdk: No such file or directory
2

The test case is an error message error, then normal stdout normal, then an incorrect grep command which returns error code 2. As you can see, the order and type of messages are still intact, so is exit code.

If you use this Perl, the message order wouldn't be the same, it groups stdout and stderr to their own group, then prints out stdout, then stderr.

$ ./chronic bash -c "echo 'error' 1>&2 ; echo 'normal' ; grep sdfk sdk" ; echo $?
normal
error
grep: sdk: No such file or directory
2

The order has been changed, but they are still in right stdout/stderr.

There are few thing which can improve my script, e.g. flatening down run_cmd to just a subshell, ( sh -c "$*" ; echo "$?" > "$tmpcode" ). It would save a few lines.

I'm currently writing more about tools from moreutils collection, more will be up soon.

Showing Last.fm Love status of currently MPD playing song in Conky

My previous post shows you how to include play count in Conky. I don't know how could I even forget Love song. Anyway, I changed lf-playcount-image.sh, now it shows the loverly heart!

Now Conky shows if this song is loved on Last.fm

In my ~/.conkyrc, I made few changes. Basically, you would need:

${execi 6 lf-playcount-image.sh}
${if_match "${execi 6 cut -f 7 -d \  "/tmp/lf-playcount-image"}" == "1"}${color red}$color $else${color black}$color $endif$

First line is to update, second one is to grab the love status, which is stored in /tmp/lf-playcount-image at field 7, space-separated. If it's 1, then this currently playing song is loved on Last.fm.

$@ as command and arguments to run in Bash script

Sometimes, your script may need to accept a command and run that command in your script. I decided to take a look at $* and $@. From manpage:

Special Parameters

* Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable. That is, "$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.

@ Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" ... If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed).

I wrote a simple script to understand better:

#!/bin/bash

echo "\$# = $# arguments"
echo

TMP=($*) ; echo -n "(\$*)   => ${#TMP[@]} elements => "
for arg in ${TMP[@]}; do echo -n "$arg, " ; done ; echo # or in $*

TMP=("$*") ; echo -n "(\"\$*\") => ${#TMP[@]} elements => "
for arg in "${TMP[@]}"; do echo -n "$arg, " ; done ; echo # or in "$*"

TMP=($@) ; echo -n "(\$@)   => ${#TMP[@]} elements => "
for arg in ${TMP[@]} ; do echo -n "$arg, " ; done ; echo # or in $@

TMP=("$@") ; echo -n "(\"\$@\") => ${#TMP[@]} elements => "
for arg in "${TMP[@]}" ; do echo -n "$arg, " ; done ; echo # or in "$@"

echo
echo 'Running:'
"$@"
$ ./test.sh echo abc '123 456' def
$# = 4 arguments

($*)   => 5 elements => echo, abc, 123, 456, def, 
("$*") => 1 elements => echo abc 123 456 def, 
($@)   => 5 elements => echo, abc, 123, 456, def, 
("$@") => 4 elements => echo, abc, 123 456, def, 

Running:
abc 123 456 def

"$@" is the one to use.

If I need to run a short shell script:

$ ./test.sh bash -c 'for((i=0;i<3;i++)); do echo -n "$i," ; done ; echo'
$# = 3 arguments

($*)   => 11 elements => bash, -c, for((i=0;i<3;i++));, do, echo, -n, "$i,", ;, done, ;, echo, 
("$*") => 1 elements => bash -c for((i=0;i<3;i++)); do echo -n "$i," ; done ; echo, 
($@)   => 11 elements => bash, -c, for((i=0;i<3;i++));, do, echo, -n, "$i,", ;, done, ;, echo, 
("$@") => 3 elements => bash, -c, for((i=0;i<3;i++)); do echo -n "$i," ; done ; echo, 

Running:
0,1,2,

Or using bash -c "$*":

$ ./test.sh echo abc '123 456' def
$ ./test.sh 'for((i=0;i<3;i++)); do echo -n "$i," ; done ; echo'

man a manpage file

Since I've learned how to compile programs, I started to not make install them onto system directory or my home directory. I often run the compiled program from its source directory. The reason I do this way is because the Makefile may not be good or not even include uninstall action. I haven't seen any couldn't be run from its compiling directory.

However, there is always one thing bothering me, the manual if they provide. I don't install, therefore no manual pages installed in man page searching path. I just figured out this way to get around:

cd /path/to/source/mandir #
ln -s . man1 # Fake a section
man -M . name_of_manpage

At first, I was proud of myself for being able to fool man. But the joy didn't last long, because I found it's silly after I read the man man's SEARCH PATH FOR MANUAL PAGES. To man a local man file, you only need to give a path with the manpage filename:

cd /path/to/source/mandir #
man ./name_of_manpage.1

Because

First of all, when the name argument to man contains a slash (/), man assumes it is a file specification itself, and there is no searching involved.

I just learned after years. Once again, there is a reason for saying RTFM!

Burning files using command-line programs

I need to burn some files, so I would have more space. I have done this with GUI tools, you just drag and drop, then hit the burn button. After some reading, it's fairly simple to do in terminal.

Basically, you create a temporary directory and move files or copy files to it, then use mkisofs to create an ISO from it. You can mount it to check if you are a very careful person, or you can cdrecord it directly.

Here is what I want to do, I want to use symbolic links to include directories which I want to burn. There is a benefit of using symbolic instead of moving files: If temporary directory and files are located in different partitions or hard disks. Symbolic links would better option since the content of files would not need to be moved.

So the following commands would do what I want:

mkdir ~/tmpdir #
cd ~/tmpdir

# Move files
mv /path/to/foo .

# Or create symbolic link
ln -s /path/to/bar

# Check size, -L option would follow symbolic links
du -shL

# Creating ISO
cd ..
# -f for following symbolic links, -J for Joliet, -r for Rock Ridge
mkisofs -Jrf -V LabelForDisc -o ~/Final.iso tmpdir

# Mount to check, iocharset=utf8 might be necessary if your filenames contain chars other than ISO-8869
sudo mount -t iso9660 -o ro,loop,iocharset=utf8 ~/Final.iso /mnt/cdrom

# Generating list for files ISO
cd /mnt/cdrom
find . | sort > /tmp/listISO
# Generating list for files on disk, -L is for following symbolic links
cd ~/tmpdir
find -L . | sort > /tmp/listDISK

# Checking for files, not size or their contents
diff /tmp/list*

# Burn it, baby!
cdrecord ~/Final.iso

I think it's pretty clear, isn't it?

(This is a post for reminding me when I need these steps in the future.)

cat loves sponge

cat loves sponge as you can see in this YouTube video, but if it's soaked with water, a sponge bathing? They would run away next time they see you hiding a sponge behind you. This video provides a nice instructions, if you want to try this one-time only bathing technique:

  1. Acquire cat and remove collar, [First step is always the hardest, they have sixth sense, they can smell your intentions!]
  2. Lightly Soap with dampened cloth (warm diluted solution of cat shampoo is best), ~ praise cat throughout ~ [I doubt they really buy it]
  3. Thoroughly rinse with warm damp cloth, then
  4. Gently towel dry. (do not iron) [I bet some owners would want to try if ironing is more efficient]

I found it's always fun to watch cat getting bathed, though there are some cat breeds do like water. And...

Wait a minute!

Why am I posting about cat bathing? Back to the topic, uhm, I mean start the topic.

Someone posted a question on Gentoo Forums about how to do cat testfile > testfile. Using cat as example to ask that question is confusing, check the followings:

# Mistakenly list Mary as John, correcting
sed 's/John/Mary/g' testfile1 > testfile1
# Who doesn't love Mary? So keep Mary only
grep Mary testfile2 > testfile2

If you have ever learned a lesson from above, you would know the files would be empty after. Of course sed has in-place editing, and I think you can use it to do grep job. Anyway, someone answered with about sponge from moreutils collection. With it, you can

sed 's/John/Mary/g' testfile1 | sponge testfile1 #
grep Mary testfile2 | sponge testfile2

Before, I always cp to /tmp, then process the one at /tmp and redirect the result to original file. I don't think I would really use it because it's mostly not installed on our system and usually I would avoid in-place processing. First, they don't save memory, they cache result and output to the file at once, you gain nothing except the coding convenience; second, if something goes wrong with your code—not only the sponge but also outputing to original file, the data file will be all or partially gone. For example,

grep -whatthehell this_file_is | sponge this_file_is #

this_file_is is empty after running with wrong options. One mistake and you test on real data file without backup, you will swear pretty hard.

Even I won't use it, I still wrote a simple Bash function for similar functionality:

sponges () { lines=() ; while read line; do lines[${#lines[@]}]="$line" ; done ; echo -n '' > "$1" ; for ((i=0;i<${#lines[@]};i++)) ; do echo "${lines[i]}" >> "$1" ; done }

# Break down for readability
sponges () {
  lines=()
  while read line; do
    lines[${#lines[@]}]="$line"
  done
  echo -n '' > "$1"
  for ((i=0;i<${#lines[@]};i++));
    do echo "${lines[i]}" >> "$1"
  done
}

Usage:

sed 's/John/Mary/g' testfile1 | sponges testfile1 #
grep Mary testfile2 | sponges testfile2

This Bash function isn't good and might have bugs. For example, if you supply with no filename as first argument. Just for fun.

My conclusion is: Sponge bathing is not recommended. :)

[Hey...... kitten kitten kitten... Your favorite tuna... (hiding sponge in another hand) where are you?]