Bash: Using an alias in an alias

I’ve been helping set up some VPSs (Virtual Private Server) and at the moment they don’t have domain names assigned. I can use the history command to find the right IP address to ssh in but that can be cumbersome. So I decided to write an alias for each server. These lines are in my extended bash config file that is called when my .profile runs.

My login is the same on each server so I could hard code this, but I made it general so I can share it with other users. I don’t like to hard code things so I thought I’d let bash find my user id and then use it in the ssh command. My first thought was to just create an alias for my login and use it in an alias for each server. But I couldn’t get it to work. I tried using just the alias since examples of creating aliases seem to work this way with builtin commands. Then I thought that maybe I need to escape the alias so that bash knows that it should process it.


alias myID='who am i | cut -d\  -f 1'
alias myserver='ssh myID@192.245.184.221'      #Doesn’t work
alias myserver='ssh $(myID)@192.245.184.221'   #Works fine

It turns out that the second line works, but I did had a problem with aliases being set and it didn’t work for me. So I started playing around with things that did work and making small adjustments. FYI, One thing you need to do if you redefine aliases and functions is to make sure that you clear out the old ones. If you don’t then you may not be running the function you thought you were or you could be running an alias when you thought you were running a function.

You can use unset to remove them from memory. Use the -f flag for functions.


unset myID
unset -f dave

For myserver, I put the alias command in parens and prefixed it with a $. Bash executes this first and then uses it in the ssh command. If you don’t do this, bash thinks that the first argument for ssh is ‘who’ and fails. That worked, but I don’t like the idea of having duplicate code in login alias. So I thought I’d try a function. In the first function, I define a variable and then use it. The third function looks an awful lot like a failed alias in the example above, but since it is a function, it works. I think what is happening is that when bash sees $(myID) it thinks, I should process whatever is inside the parens. So it finds the alias and runs it.


#   Log in to other systems
alias myID='who am i | cut -d\  -f 1'

alias myserver='ssh $(who am i | cut -d\  -f 1)@192.245.184.221'
dave() { id=$(who am i | cut -d\  -f 1); ssh $id@192.233.221.11; }
dana()  { ssh $(myID)@192.149.142.163 ; }

alias bigserver='ssh "echo $(myID)"@192.255.174.220'

It finally occurred to me that the .profile script is a full blown shell script. So that means I can use variables in it. I define an id variable that is the result of the shell executing the commands that are in it. When the .profile is run, bash assigns id to my login. Then I can use it anywhere I want with $id. I don’t need to use ${id} since my login is one word and since the @ cannot be in a variable name, bash knows to stop processing when it hits the @. This is what I wanted to do with aliases. I thought about making it a local variable since I don’t need it to hang around outside of this script. But the alias isn’t resolved until it is used, so it needs to resolve the variable when it is called.


#   Log in to other systems
id=$(who am i | cut -d\  -f 1)
alias myserver='ssh $id@192.245.184.221'
alias dave='ssh $id@192.233.221.11'
alias dana='ssh $id@192.149.142.163'

I started playing around with aliases again and got strictly aliases to work as well.


#   Log in to other systems
alias myID='who am i | cut -d\  -f 1'
alias myserver='ssh $(myID)@192.245.184.221'
alias dave='ssh $(myID)@192.233.221.11'
alias dana='ssh $(myID)@192.149.142.163'

Update: I was playing with printenv and it turns out that one of the environment variables is LOGNAME. So you can replace the $id with $LOGNAME in the code above.

Notes on setting up a server.

I’m setting up a new Ubuntu server and while most of the defaults are fine, there are some things that I need to adjust. I have a very shallow understanding of this stuff, so there could be better and more secure ways to do this, but this works for me.

Disallow access to PHP include files

There isn’t any reason that people need to see the include files that I use in my websites. You could name them .inc.php so that the raw code isn’t available, but that’s not very elegant, and outsiders can still access the file. There isn’t anything particularly sensitive in them, but by themselves, they don’t display correctly. So I added a few lines to my /etc/apache2/apache2.conf file. Just below the section that disallows viewing .htacess files.


#
# The following lines prevent .htaccess and .htpasswd files from being 
# viewed by Web clients. 
#
<Files ~ "^\.ht">
    Order allow,deny
    Deny from all
    Satisfy all
</Files>

# The following lines prevent .inc files from being
# viewed by Web clients.
#
<Files ~ "\.inc$">
    Order allow,deny
    Deny from all
</Files>
#

Prevent directory browsing

If you have a bunch of images in a directory, then anyone who wants can view all of them just by looking at the web page source and putting the directory name after your URL. I’d rather they not do that, so I restrict listing of the files by adding this line to my /etc/apache2/httpd.conf file. On my default Ubuntu install this file is empty.


Options Includes FollowSymLinks MultiViews

Restart Apache for the changes to take effect.

Alternate method to prevent directory browsing

If you want to prevent directory browsing in just one directory and either don’t want to change the whole site or don’t have access to the files named above, add this line to your .htaccess file.


Options -Indexes

Probably don’t have to restart Apache for changes to take effect.

Prevent Directory Browsing on a Per Site Basis

Changing the httpd.conf file will change the behavior of all sites on your server. If you want to change the behavior of just one site, edit its file in /etc/apache2/sites-avalilable. Find the line that has Options FollowSymLinks in it and if it has Indexes in it, delete it. This is what the default Ubuntu install has.


  <Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
  </Directory>

Probably do have to restart Apache for changes to take effect.

Prevent access to your include directory

Add this to your site’s file in /etc/apache2/sites-avalilable.


#<Directory /www/MySite/include.php>
#    Deny from all
#</Directory>

Show an error document instead of the default 404 error

Create a normal php document with your sites navigation and a message that says the file can’t be found and maybe you can find it with the nav menus. Add this to your site’s file in /etc/apache2/sites-avalilable. And while you are at it, there is no reason you need to tell anyone that they don’t have permission to see a particular file, just tell them it’s not found, so add the same line for a 403 error. I take them back to the main page and display the missing file in the main page.


ErrorDocument 404 /index.php?p=missing
ErrorDocument 403 /index.php?p=missing

Removing subviews from a view.

In my apps, I use swipe gestures to move to the next screen. The old stuff is animated off to the left and the new stuff animates in from the right. If the user rotates the device, I need to remove the elements of the view from the main view and redisplay them in the new orientation. I also need to remove the animations if they are occurring. I have lots of different layouts, but each layout has this method in it. (The contents vary a bit.)

I first remove the animations. Then I remove the views from the main view. Then I set the views to nil to make sure their resources are released.


- (void)removeAllObjectsFromParentView {
    [self.wordView.layer removeAllAnimations];
    [self.categoryView.layer removeAllAnimations];
    
    [self.wordView removeFromSuperview];
    [self.categoryView removeFromSuperview];
   
    self.wordView = nil;
    self.categoryView = nil;
}

All of the subviews of wordView and categoryView will be removed and released when the parent view is removed and released.

Pick a random color in iOS

In one of my methods I wanted to put randomly colored text on the screen. I put this class method in my Utilities.m class.


#define ARC4RANDOM_MAX      0x100000000
+ (UIColor *) randomColor {
    CGFloat red =  (CGFloat)arc4random()/ARC4RANDOM_MAX;
    CGFloat blue = (CGFloat)arc4random()/ARC4RANDOM_MAX;
    CGFloat green = (CGFloat)arc4random()/ARC4RANDOM_MAX;
    return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}

To set the color, just call


label.textColor = [Utilities randomColor];

Lazy Instantiation in iOS

I had a bunch of code like this in the init for my first view controller. Paul Hegerty had mentioned lazy instantiation in his Stanford CS193 courses. And the boilerplate code in the AppDelegate for creating Managed Object Contexts and Persistent Store Coordinator uses it a log. So I understood the concept, but I hadn’t used it for my own globals. After listening to his most recent class, I decided to convert all of my init code to lazy instantiation.

One benefit of lazy instantiation is that you don’t allocate resources until you need to use the object. In my case, a better reason is that I’m not cluttering up my view controller with code that initializes global variables. In the Model, View, Controller design pattern, initialization code really doesn’t belong in the controller. But even more important, since I check for initialization in the class that creates the variable, I can’t forget to initialize the variable.

Old Code


    if (![Globals sharedInstance].showmePict ) {
        [Globals sharedInstance].showmePict = @"Either";
    }
    
    if (![Globals sharedInstance].targetSoundDelay ) {
        [[Globals sharedInstance] resetTargetDelay:TARGET_SOUND_DELAY];
    }
    

    if ( !([Globals sharedInstance].targetSound) ){
#ifdef SHOWME_TARGET_SOUND
        [[Globals sharedInstance] resetTargetSound:SHOWME_TARGET_SOUND];
#endif
    
#ifndef SHOWME_TARGET_SOUND
        [[Globals sharedInstance] resetTargetSound:@""];
#endif
    }

New Code

In the singleton for globals.


- (NSString *)showmePict {
    
    if (!_showmePict ) _showmePict = @"Either";
    return _showmePict;
}

- (NSUInteger)targetSoundDelay {
    
    if ( !_targetSoundDelay ) [self resetTargetDelay:TARGET_SOUND_DELAY];
    return _targetSoundDelay;
}

- (NSString *)targetSound {
    
    if ( !_targetSound ){
    #ifdef SHOWME_TARGET_SOUND
        [self resetTargetSound:SHOWME_TARGET_SOUND];
    #endif
        
    #ifndef SHOWME_TARGET_SOUND
        [self resetTargetSound:@""];
    #endif
    }
    return _targetSound;
}