Showing posts with label zsh. Show all posts
Showing posts with label zsh. Show all posts

Friday, August 31, 2012

My zsh prompt string PS1

Every Unix user with a blog has a post about it: how they have configured their PS1 so their command prompt is almost a mini dashboard that shows everything they’d need to know.  I am no exception.  Even if this post doesn’t really help others, bragging is gratifying, so I’d go on and show how awesome my PS1 is :)

I have been using zsh for a while now, and I am quite happy with it.  I’m a sucker for colours and I was annoyed that I couldn’t take full advantage of my 256 colour terminal because I just didn’t know how to.  So I searched the web and found two good pages.  Combined with some manual-reading I had done, I cooked up my shiny new PS1:
PS1='%(?.%F{245}.%F{124})%2m %F{130}%~ %F{215}%# %f'
Unfortunately, prompt strings are never intuitive, so I’ll do some explaining.
  • %F{colour_code} sets the foreground colour to the specified colour.  Colour code is simply a number in the range 0 to 255 (assuming your terminal supports 256 colours).
  • %f resets the foreground colour to default.
  • %(condn.true_string.false_string) is a conditional expression.  If condn evaluates to true, true_string is emitted; otherwise false_string is emitted.  Like you’d expect, you can have some special strings in condn to mean interesting things.
Deconstructing my PS1
I actually started with something more simple:
PS1='%F{245}%2m %F{130}%~ %F{215}%# %f'
The prompt string starts with the machine name printed in grey.  Then comes the current working directory in a yellow/orange colour, followed by zsh’s % prompt that’s a wee bit brighter than the directory name.  But I wanted to have the exit status of the previous command in my prompt somewhere.  It’s not some information I really need, but I thought it can be a nice-to-have.  But at the same time I didn’t want this information to clutter up my prompt.

I figured I could use make use of colours to differentiate between success and failure of the previous command.  So I changed %F{245}%2m to %(?.%F{245}.%F{124})%2m.  Now, the machine name is printed in grey if the previous command succeeded, and in red if it had failed.  Thus born the prompt string that exactly fits my needs... for now.

Friday, August 10, 2012

Only 8 colours on ssh-ed terminals?

Vim (i.e. console vim, but not gvim) on my primary workstation uses 256 colours and with syntax highlighting on, editing source code is a much pleasant experience (vs. being limited to 8 colours).

However, every time I ssh a different machine, Vim on the remote machine always used 8 colours.  I’m a big fan of seeing a lot of colours and hated the 8-colours environment of the remote machines.  After some digging, I found out the cause for this problem.  My primary machine has COLORFGBG environment variable set, but the remote machines don’t.  Check Skåne Sjælland Linux User Group to see how you can fix this issue.  (Essentially, you’ll have to configure ssh on your machine and the remote machine to pass COLORFGBG environment variable.)

Update: Actually, adding the line set background=dark to my ~/.vimrc seems like a much easier solution.  And also, “only 8 colours” is not the real problem.  Real problem is that vim is using a different set of colours (dark blue instead of light blue, for instance) on the new machine.

Wednesday, June 13, 2012

Renaming files using pattern matching

Let’s say you have a bunch of files in a directory with names like dog.jpeg, cat.jpeg, fountain.jpeg, etc.  Your OCD insists that you rename them all to more “regular” names like dog.jpg, cat.jpg, and fountain.jpg.  If you use zsh, pat yourself on the back for using a cool shell, and run these commands:
autoload zmv
zmv -W '*.jpeg' '*.jpg'
You probably know this already, but it’s worth mentioning.  Single quotes around the patterns is important because we want zmv to interpret the pattern; zsh should pass the pattern as such to zmv without expanding it.

Wednesday, January 18, 2012

Inserting single quote character inside a single-quoted string

“Strings” (not the same as strings in languages like C or Python) in shells (bash and zsh especially) can be enclosed within either single quotes (') or double quotes (").  Double quotes escape (i.e. remove special meaning from) characters like space and single quotes, but single quotes remove meaning from almost every special character including spaces, $, !, new lines, etc.  (Learn more)

You can use \ escape to insert a double quote character (") inside a double-quoted string, but you cannot do the same for inserting a single quote character (') inside a single-quoted string.  Because \ has no special meaning inside a single-quoted string.
% echo "abc \" def"
abc " def
% echo 'abc \' def'                   
quote> blah'
abc \ def
blah
%
But I often find myself needing to insert apostrophes inside single-quoted strings.  I use the following syntax then:
% echo 'There'\''s always a way out'
There's always a way out
Shells usually let you concatenate strings by just writing them together without any space between them: "abc""def" is the same as "abcdef" which is the same as "abc"'def' which is the same as abcdef (without quotes).  We use the same technique here: we have two different single-quoted strings: 'There' and 's always a way out'.  In between them we have a single-quote character escaped with \ to mean that we want a literal apostrophe character inserted.

Sunday, October 23, 2011

zsh syntax highlighting

I like colours.  I have aliased all frequently used commands like ls, grep, etc. by adding flags to show colours in the output.  I have set my PS1 in such a way that the prompt is in a different colour.  It makes it easy for me to see where the prompt ends and the command starts.  When I heard about fish and that it does syntax highlighting, I was tempted to switch to it.  But I couldn’t because it didn’t do certain things I needed.  (I don’t remember the details now.)  Soon after that I found out that it’s very simple to add syntax highlighting to zsh!

All you have to do is download the code from zsh-syntax-highlighting project and “source” it in your .zshrc.  But I wasn’t happy with their defaults.  By default this script underlines path names, but I hate underlining because it makes text less readable.  I also didn’t like their choice of blue colour for globs.  On my black terminal, blue is hardly readable.  Customising the formatting was easy too; I only had to change the value of a variable.  This is what my .zshrc has now, and syntax highlighting works like a charm.
source ~/dload/src/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
ZSH_HIGHLIGHT_STYLES[globbing]='fg=yellow'
ZSH_HIGHLIGHT_STYLES[path]='bold'
You can find the list of different syntax highlighting options in this file.

Thursday, October 20, 2011

Terminating scripts when any individual command fails

Let’s say you have a script that builds a project, runs all tests, and pushes the binary to a staging/production server.  If the build fails or a test fails you’d want the script to stop immediately.  Pushing a binary that failed some tests is obviously wrong.  You can check for a command’s return value using if and terminate your script.  But doing that for every command in the script would make your script less readable and more prone to bugs.

Shells provide a clean solution for this use case: you can set a script-level option to stop the script execution if any command you invoke from the script exits with a non-zero status.  You do that in bash using
set -e
and in zsh using
setopt err_exit
So your script would essentially look like this:
#!/bin/bash
set -e
make
make test
make push
zsh also has a err_return option that can be set to make a function return (as opposed to terminating the whole script) when a command invoked by a function fails.

Sunday, July 31, 2011

Finding information about commands you use

Q: How to find out where the binary of a command I'm running?
A: You can use type command (available on both zsh and bash):
% type ls
ls is an alias for ls -h --color=auto
% type cat
cat is /bin/cat
% type alias
alias is a shell builtin

Q: Sometimes I want to know if a command is a shell script or a compiled binary.  How do I do that?
A: If you use zsh, you can use =command to get to command's full path.
% file =backup
/home/manki/d/bin/backup: a /bin/rbash script text executable
% # To show you what =backup actually translates to
% echo =backup
/home/manki/d/bin/backup
If you use bash, you can use type command within backticks or the equivalent $(...).
$ file `type -p backup`
/home/manki/d/bin/backup: a /bin/rbash script text executable
When you have aliases, this can get tricky.  On bash I don't know how to do this, but zsh is smart enough to find the executable even when you have aliases set up.  For instance, I have aliased ls to ls -h --color=auto, but zsh gives me the right binary for =ls:
% file =ls    
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped

Saturday, May 14, 2011

pushd, popd, and dirs commands

If you spend a lot of time in the command line, you will have to cd to different directories frequently.  Instead of the cd you can use pushd command to switch to a different directory.  pushd command pushes the new directory into a "directory stack".  Later you can return to your original location using popd command.  Both pushd and popd print the latest contents of the directory stack.  You can also use dirs command see the directory stack.  Items in the directory stack are indexed from 0.  You can reference any directory in the stack by typing ~ followed by the index.  A sample session using the directory stack (user input is in bold font):
~% pushd /tmp
/tmp ~
/tmp% pushd /etc
/etc /tmp ~
/etc% pushd /var
/var /etc /tmp ~
/var% dirs
/var /etc /tmp ~
/var% popd
/etc /tmp ~
/etc% pwd
/etc
/etc% dirs
/etc /tmp ~
/etc% cd ~1
pingala /tmp % dirs
/tmp /etc ~
As the directory stack becomes larger, finding the index of a directory in the stack becomes harder.  I use -v flag to print the numeric index along with each item:
/tmp% dirs -v
0       /tmp
1       /etc
2       ~
If you are like me, you wouldn't remember to use pushd.  For users like us, the zsh has an auto_pushd option.  When it's set, all cd commands are treated like pushd commands.  With that option enabled, I use the shell normally, but zsh remembers the directories I have recently been to.  I can use dirs -v any time to get a list of recent directories and use the ~ notation to return to one those directories.

Sunday, April 24, 2011

Counting files

I have organized my photos this way: there's a Photos directory, under which there's one subdirectory for each year.  In addition to the JPEG images, these directories also contain files like Picasa.ini.  So, to find out how many pictures I have I can use the find command:
~/d/Photos % find . -type f -iname \*.jpg | wc -l
5101
This command counts the files that have .jpg file extension.  Let's say I want to know the years in which I took more photos, and the years in which I took relatively lesser number of photos.  That can be done using the following command:
% for year in *
do
  echo -n "$year "
  find $year -type f -iname \*.jpg | wc -l
done
2006 98
2007 983
2008 1978
2009 1128
2010 749
2011 165
If you use zsh, you can modify the command to include only the last few years:
% for year in <2009-2011>                   
do
  echo -n "$year "
  find $year -type f -iname \*.jpg | wc -l
done
2009 1128
2010 749
2011 165

Wednesday, February 23, 2011

Entering command mode in shells with vi bindings

If you use zsh or bash with vi key bindings, you know that by default the shell is in insert mode.  Every time you need to run some editing command you would first press Esc to enter command mode.  I recently found that just pressing Alt along with the first key in your command will put you in command mode.

For example, if you want to go up in command history, press Alt+k.  That will put you in command mode and take you to the previously run command.  From there on, pressing k will scroll up the command history.  One very useful shortcut I use is Alt+I (which is Alt+Shift+i): this is equivalent to Ctrl+a in Emacs mode or pressing the Home key.

Tuesday, February 22, 2011

Quick access to frequently visited directories

Define hashes for directories you frequently cd to from the command line, and access them as ~hash_name.  For example, my .zshrc has:
hash -d fx=~/dload/app/comm/firefox
hash -d prog=~/d/prog
hash -d personal=~/d/personal
cd ~fx will take me to ~/dload/app/comm/firefox, cd ~prog will take me to ~/d/prog, and so on.