Monday, January 31, 2011

Unix spoken here (3)

Let's do some more with Unix commands. Previous posts here and here.

You might have noticed that I have a very short prompt in Terminal, just the character `>` followed by a space. That's because I have an (invisible) file in my home directory named .bash_profile, containing this line:


> cat ~/.bash_profile
PS1="> "; export PS1


We can change back to the original behavior by changing the name of the file or moving it, and then re-starting Terminal:


mv ~/.bash_profile ~/Desktop/x.txt



c-98-236-78-154:Desktop telliott$


We could comment out that line of the file (now that it's visible with no leading `.` in the filename, we can easily open it in TextEdit).


#PS1="> ";  export PS1


But I like it, so I'll revert to the original situation.

I think there is a way to get TextEdit to show hidden files in the Open dialog, but I forget how at the moment. In any event, it's interesting that having opened and closed the file while it was visible on the Desktop, Open Recent now shows .bash_profile as an option.

Another way to edit the file is to use pico (a command line editor):


> pico ~/.bash_profile


I'm sure you can figure out how to use pico.

One more useful thing in .bash_profile is modification to the $PATH variable. Let's look at that from the command line:


> echo $PATH
/opt/local/bin:/opt/local/sbin:/Users/telliott/bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/ncbi/blast/bin:/usr/texbin:/usr/X11/bin


When I call ls, for example, these directories are searched in turn until the program is discovered finally. It is in:


> whereis ls
/bin/ls


If I wanted to modify the $PATH variable I could do:


> PATH=~/Desktop:$PATH;  export PATH
> echo $PATH
/Users/telliott/Desktop:/opt/local/bin:/opt/local/sbin:/Users/telliott/bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/ncbi/blast/bin:/usr/texbin:/usr/X11/bin


My $PATH is pretty complicated for several reasons: use of MacPorts, some things related to ncbi and X11, etc. It would take too long to explain.

The shell has command history. If you want to repeat a command, just press the up arrow key. To repeat a command further back, keep pushing until you see the one you want. Then press Return. Suppose you have a series of commands (say 3 in a row), and the first of these is 6 commands ago. Press the arrow 6 times, then return. Press the arrow 6 times, then return. Press the arrow 6 times, then return. Because the command history is updated each time, 6 arrow presses gets you back exactly where you need to be for each of the three commands in the sequence.

Alternatively you could do history:


> history
501 cd Desktop
502 pico ~/bash_profile
503 echo $PATH
504 file ls
505 whereis ls
506 PATH=~/Desktop:$PATH; export $PATH
507 PATH=~/Desktop:$PATH; export PATH
508 echo $PATH
509 history


which lists each command I've entered since the last time I booted my machine (even saves between Terminal sessions). One can select a command from the history by number or by partial name match:


> !505
whereis ls
/bin/ls
> !wh
whereis ls
/bin/ls


First it prints the command, and then the result. You can combine commands on a single line with `;`


> !504; !512
whereis ls; echo abc
/bin/ls
abc
> history | tail -2
515 whereis ls; echo abc
516 history
> !515
whereis ls; echo abc
/bin/ls
abc


We're getting pretty good with this stuff! We don't really want to list all 500+ commands in the history, just the last two. So we feed the results of history to the Unix tool called tail through a `|`, a Unix "pipe." tail takes an option for how many lines at the end of the "file" to display.

The Unix command grep searches lines of text for those matching a pattern. It's particularly useful in combination with `|`.


> history | grep pico
40 pico ~/.hgrc
298 pico
502 pico ~/bash_profile
515 history | grep pico


Here, I edited some file with pico but I can't remember which one it was. So the entire output of the history is fed through the pipe to grep, searching for lines containing the pattern. Now I can just do:


> !40
pico ~/.hgrc


I won't show you the file.

The last command for this post is ln with the option -s which makes a "soft link" to a file. I can never remember, but you want to put the existing file first (that's the opposite of the way I would design it):


> cat temp/y.txt
echo "abc"
> ln -s foo temp/y.txt
ln: temp/y.txt: File exists
> ln -s temp/y.txt foo
> ls
foo temp
> cat foo
echo "abc"
> ls -l
total 8
lrwxr-xr-x 1 telliott staff 10 Jan 31 07:53 foo -> temp/y.txt
drwxr-xr-x 5 telliott staff 170 Jan 31 07:53 temp


Now the listing for foo shows an `l` as the first character in the file mode, and it also shows -> temp/y.txt, where it links to.