Entries in Computing (187)

Friday
Jul202012

Why .profile, .bash_profile, and .bash_login?

Configuring the startup of bash can seem to be an unnecessarily complex process with a plethora of configuration files all being read or ignored according to a mystifying set of rules.  The Invocation section of bash man page describes these rules in some detail but gives very few clues as to the reasons for their existence and the problems they were intended to solve. It is common for a change to one of the configuration files either to have no effect at all (ie: to be ignored), or to have unintended side-effects in another type of start-up.  Indeed, a search of Linux and Unix forums will reveal many threads dealing with of this type of problem.

This post is part of an attempt by me to understand the complexities of bash startup and to draw up some rules to simplify the use of the configuration files.  This attempt was inspired by installing RVM, the Ruby Version Manager which screwed up my Bash settings by creating a .bash_login file.  And this particular post was inspired by reading the following two paragraphs from Learning the bash Shell, 2nd Edition, Cameron Newham and Bill Rosenblatt, O'Reilly, 1998, page 59:

bash allows two synonyms for .bash_profile: .bash_login, derived from the C shell's file named .login, and .profile, derived from the Bourne shell and Korn shell files named .profile. Only one of these three is read when you log in. If .bash_profile doesn't exist in your home directory, then bash will look for .bash_login. If that doesn't exist it will look for .profile.

One advantage of bash's ability to look for either synonym is that you can retain your .profile if you have been using the Bourne shell. If you need to add bash-specific commands, you can put them in .bash_profile followed by the command source .profile. When you log in, all the bash-specific commands will be executed and bash will source .profile, executing the remaining commands. If you decide to switch to using the Bourne shell you don't have to modify your existing files. A similar approach was intended for .bash_login and the C shell .login, but due to differences in the basic syntax of the shells, this is not a good idea.

From these paragraphs I draw the following conclusions:

  • .bash_login was introduced as part of an unsuccessful attempt at compatibility with the C shell.  This suggests that its use can be and should be avoided.  You should never create a .bash_login and if you have one you should not allow it to be activated (creating a .bash_profile should ensure this) and should delete it.
  • .bash_profile is the appropriate place to put bash-specific commands to be run when starting a login shell.  It would seem sensible to always have a .bash_profile file and to never allow bash to read .profile directly.  Instead the .bash_profile should always end with a command to include the .profile, but rather than just source .profile it would be better to test .profile exists first, for example:
    if [ -f "$HOME/.profile" ]; then
      source "$HOME/.profile"
    fi
    
    Always having a .bash_profile has the additional benefit of preventing any .bash_login from running. 

The problems of what bash-specific commands are appropriate to a login shell and, indeed, what a login shell is and how it differs from a non-login shell, I will leave for another post.

Thursday
Jul122012

How to set the Position and Size of Windows in Ubuntu 12.04 LTS

A few web pages that I found useful when writing scripts to set up windows with particular positions and sizes in Ubuntu 12.04 LTS:

Automate opening of nautilus windows? - a thread at Ubuntu Forums

wmctrl examples from Spiral of Hope

wmctrl(1) - Linux Man Page

In the Spiral of Hope page I am not sure what the constructs of the form [#r -r] are meant to do.  Maybe they were part of the option syntax for earlty versions of wmctrl?  Anyhow, the examples seem to work if you replace each occurrence of [#r -r] by just -r (and similarly for the other [#..] constructs).

Thursday
Jul122012

How to Create Launchers in Ubuntu 12.04 LTS

A few of web pages that I found useful when I wanted to create a launcher for a shell script in Ubuntu 12.04 LTS:

Desktop files: putting your application on the desktop menus from Gnome Dev Center

Desktop Entry Specification from freedesktop.org

Registered Categories in Desktop Menu Specification from freedesktop.org

Tuesday
Jul102012

Useful Blog Posts

A couple of blog posts that I found useful when setting up my development web-server on Ubuntu 12.04 LTS:

How to Install a PHP PECL extension/module on Ubuntu by Mark Foster

Send-only Mail Server with Exim on Ubuntu 10.04 LTS (Lucid) by Phil Paradis

Friday
Jun292012

Unit Test Examples

Here is a set of simple unit tests in various programming languages.  Each one performs two tests on the string length function. They are intended as simple examples of how to get started unit testing.

This was partly inspired by a blog post by Mike Clarke,

Ruby (string_test.rb)

require 'test/unit'
class StringTest < Test::Unit::TestCase
  def test_length_of_empty_string
    assert_equal(0, "".length)
  end
  def test_length_of_nonempty_string
    s = "abcd"
    assert_equal(4, s.length)
  end
end

PHP (string_test.php)

<?php
require_once 'PHPUnit/Autoload.php';
class String_Test extends PHPUnit_Framework_TestCase
{
	public function test_length_of_empty_string() {
		$this->assertEquals(0, strlen(''));
	}
	public function test_length_of_nonempty_string() {
		$this->assertEquals(4, strlen('abcd'));
	}
}
// End of file

Python (string_test.py)

import unittest
class StringTest(unittest.TestCase):
  def testLengthOfEmptyString(self):
    self.assertEquals(len(""), 0)
  def testLengthOfNonEmptyString(self):
    self.assertEquals(len("abcd"), 4)
if __name__ == '__main__':
  unittest.main()

Haskell (string_test.hs)

import Test.HUnit
test1 :: Test
test1 = TestCase (assertEqual "Length of Empty String" 0 (length ""))
test2 :: Test
test2 = TestCase (assertEqual "Length of Nonempty String" 4 (length "abcd"))
compositeTest :: Test
compositeTest = TestList [TestLabel "test1" test1, TestLabel "test2" test2]
main :: IO ()
main = do
         _ <- runTestTT compositeTest
         return ()

Java (StringTest.java)

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class StringTest {
	@Test
	public void testLengthOfEmptyString() {
		assertEquals("LengthOfEmptyString", 0, "".length());
	}
	@Test
	public void testLengthOfNonemptyString() {
		assertEquals("LengthOfNonemptyString", 4, "abcd".length());
	}
}

Now a Bash script to run them all. This was written for my particular Ubuntu 10.04 LTS setup. You might need to modify it for other setups and other operating systems.

#!/bin/bash
rule="======================================================================"
echo $rule
echo "RUBY:"
ruby string_test.rb
echo $rule
echo "PHP:"
phpunit string_test.php
echo $rule
echo "PYTHON:"
python string_test.py
echo $rule
echo "HASKELL:"
runhaskell string_test.hs
echo $rule
echo "JAVA:"
javac -cp /usr/share/java/junit4.jar StringTest.java
java -cp /usr/share/java/junit4.jar:. org.junit.runner.JUnitCore StringTest
echo $rule

Running this script gives the following output:

======================================================================
RUBY:
Loaded suite string_test
Started
..
Finished in 0.00063 seconds.

2 tests, 2 assertions, 0 failures, 0 errors
======================================================================
PHP:
PHPUnit 3.6.10 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 2.75Mb

OK (2 tests, 2 assertions)
======================================================================
PYTHON:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
======================================================================
HASKELL:
Cases: 2  Tried: 2  Errors: 0  Failures: 0
======================================================================
JAVA:
JUnit version 4.8.1
..
Time: 0.013

OK (2 tests)

======================================================================

In order to test the tests I reran the script after editing each test file to change the expected length of the nonempty string from 5 to 4 (without changing the nonempty string itself). This gave the following output:

======================================================================
RUBY:
Loaded suite string_test
Started
.F
Finished in 0.025308 seconds.

  1) Failure:
test_length_of_nonempty_string(StringTest) [string_test.rb:11]:
<5> expected but was
<4>.

2 tests, 2 assertions, 1 failures, 0 errors
======================================================================
PHP:
PHPUnit 3.6.10 by Sebastian Bergmann.

.F

Time: 0 seconds, Memory: 2.75Mb

There was 1 failure:

1) String_Test::test_length_of_nonempty_string
Failed asserting that 4 matches expected 5.

/home/tristram/Desktop/learn-by-test/string_test.php:13

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
======================================================================
PYTHON:
.F
======================================================================
FAIL: testLengthOfNonEmptyString (__main__.StringTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "string_test.py", line 9, in testLengthOfNonEmptyString
    self.assertEquals(len("abcd"), 5)
AssertionError: 4 != 5

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)
======================================================================
HASKELL:
### Failure in: 1:test2
Length of Nonempty String
expected: 5
 but got: 4
Cases: 2  Tried: 2  Errors: 0  Failures: 1
======================================================================
JAVA:
JUnit version 4.8.1
..E
Time: 0.017
There was 1 failure:
1) testLengthOfNonemptyString(StringTest)
java.lang.AssertionError: LengthOfNonemptyString expected:<5> but was:<4>
	at org.junit.Assert.fail(Assert.java:91)
	at org.junit.Assert.failNotEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:126)
	at org.junit.Assert.assertEquals(Assert.java:470)
	at StringTest.testLengthOfNonemptyString(StringTest.java:14)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:24)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:117)
	at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98)
	at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53)
	at org.junit.runner.JUnitCore.main(JUnitCore.java:45)

FAILURES!!!
Tests run: 2,  Failures: 1

======================================================================