Posts tagged ‘UNIX’

Hashbang Hacks: Parameters for Python

2013-08-18 14:00

This:

  1. #!/bin/sh

is an example of hashbang. It’s a very neat Unix concept: when placed at the beginning of a script, the line starting with # (hash) and ! (bang) indicates an interpreter that should be chosen when running the script as an executable. Often used for shells (#!/bin/bash, #!/bin/zsh…), it also works for many regular programming languages, like Ruby, Python or Perl. Some of them may not even use # as comment character but still allow for hashbangs, simply by ignoring such a first line. Funnily enough, this is just enough to fully “support” them, as the choice of interpreter is done at the system level.

Sadly, though, the only portable way to write a hashbang is to follow it with absolute path to an executable, which makes it problematic for pretty much anything other than /bin/*sh.
Take Python as an example. On many Linuxes it will be under /usr/bin/python, but that’s hardly a standard. What about /usr/local/bin/python? ~/bin/python?… Heck, one Python I use is under /usr/local/Cellar/python/2.7.3/bin – that’s installed by Homebrew on OS X, a perfectly valid Unix! And I haven’t even mentioned virtualenv

This madness is typically solved by a standard tool called env, located under /usr/bin on anything at least somewhat *nixy:

  1. #!/usr/bin/env python

env looks up the correct executable for its argument, relying on the PATH environmental variable (hence its name). Thanks to env, we can solve all of the problems signaled above, and any similar woes for many other languages. That’s because by the very definition, running Python file with the above hashbang is equivalent to passing it directly to the interpreter:

  1. $ cat >hello.py «EOF
  2. #!/usr/bin/env python
  3. print "Hello, world"
  4. EOF
  5. $ chmod a+x hello.py
  6. $ ./hello.py
  7. Hello, world
  8. $ python hello.py
  9. Hello, world

Now, what if you wanted to also include some flags in interpreter invocation? For python, for example, you can add -O to turn on some basic optimizations. The seemingly obvious solution is to include them in hashbang:

  1. #!/usr/bin/env python -O

Although this may very well work, it puts us again into “not really portable” land. Thankfully, there is a very ingenious (but, sadly, quite Python-specific) trick that lets us add arguments and be confident that our program will run pretty much anywhere.

Here’s how it looks like:

  1. #!/bin/sh
  2. """"exec python -O "$0" "$@";" """

Understandably, it may not be immediately obvious how does it work. Let’s dismantle the pieces one by one, so we can see how do they all fit together – down not just to every quotation sign, but also to every space.

The Subshell Gotcha

2013-05-20 13:13

Many are the quirks of shell scripting. Most are related to confusing syntax, but some come from certain surprising semantics of Bash as a language, as well as the way scripts are executed.
Consider, for example, that you’d like to list files that are within certain size range. This is something you cannot do with ls alone. And while there’s certainly some awk incantation that makes it trivial, let’s assume you’re a rare kind of scripter who actually likes their hacks readable:

  1. #!/bin/sh
  2.  
  3. min=$1
  4. max=$2
  5.  
  6. ls | while read filename; do
  7.     size=$(stat -f %z $filename)
  8.     if [ $size -gt $min ] && [ $size -lt $max ]; then
  9.         echo $filename
  10.     fi
  11. done

So you use an explicit while loop, obtain the file size using stat and compare it to given bounds using a straightforward if statement. Pretty simple code that shouldn’t cause any troubles later on… right?

But as your needs grow, you find that you also want to count how many files fall within your range, and how many do not. Given that you have an explicit if, it appears like a simple addition (in quite literal sense):

  1. matches=0
  2. misses=0
  3. ls | while read filename; do
  4.     size=$(stat -f %z $filename)
  5.     if [ $size -gt $min ] && [ $size -lt $max ]; then
  6.         echo $filename
  7.         ((matches++))
  8.     else
  9.         ((misses++))
  10.     fi
  11. done
  12.  
  13. echo >&2 "$matches matches"
  14. echo >&2 "$misses misses"

Why it doesn’t work, then? Because clearly this is not the output we’re looking for (ls_between is our script here):

  1. $ ls -al
  2. total 25296
  3. drwxrwxr-x  19 xion  staff       646 15 Apr 18:44 .
  4. drwxrwxr-x  15 xion  staff       510 20 May 11:15 ..
  5. -rw-rw-r--   1 xion  staff        16 10 May  2012 hello.py
  6. -rw-rw-r--   1 xion  staff      4005 28 May  2012 keyword_stats.py
  7. -rw-rw-r--   1 xion  staff       218  5 Aug  2012 magical.py
  8. -rw-rw-r--   1 xion  staff     19901 11 May  2012 space_invaders.py
  9. $ ls_between 1024 10241024
  10. keyword_stats.py
  11. space_invaders.py
  12. 0 matches
  13. 0 misses

It seems that neither matches nor misses are counted properly, even though it’s clear from the printed list that everything is fine with our if statement and loop. Wherein lies the problem?

Tags: , , , ,
Author: Xion, posted under Applications, Programming » 2 comments

Unintended Consequences

2012-10-02 12:13

In Unix-like systems, files and directories with names starting from ‘.’ (dot) are “hidden”: they don’t appear when listed by ls and are not shown by default in graphical file browsers. It doesn’t seem very clear why there is such a mechanism, especially when we have extensive chmod permissions and attributes which are not tied to filename. Actually, that’s one of distinguishing features of Unix/Linux: there are neither .exe nor .app files, just chmod +x.

But, here it is: name-based visibility control for files and directories. Why such a thing was ever implemented in the first place? Well, it turns out it was purely by accident:

Long ago, as the design of the Unix file system was being worked out, the entries . and .. appeared, to make navigation easier. (…) When one typed ls, however, these entries appeared, so either Ken [Thompson] or Dennis [Ritchie] added a simple test to the program. It was in assembler then, but the code in question was equivalent to something like this:

  1. if (name[0] == '.') continue;

That test was meant to filter out . and .. only. Unintentionally, though, it ruled out a much bigger class of names: all that start with a dot, because that’s what it actually checked for. Back then it probably seemed like an innocuous detail. Fast-forward a couple of decades and it’s a de facto standard for storing program-specific data inside user’s home path. They can grow quite numerous over time, too:

  1. $ ls -A1 ~ | grep '^\.' | wc -l
  2. 113

That’s over 100 entries, a vast majority of my home directory. It’s neither elegant nor efficient to have that much of app-specific cruft inside the most important place in the filesystem. And even if GUI applications tend to collectively use a single ~/.config directory, the tradition to clutter the root $HOME path is strong enough to persist for foreseeable future.

Heed this as a warning. In the event your software becomes a basis for many derived solutions, future programmers will exploit every corner case of every piece of logic you have written. It doesn’t really matter what you wanted to put into your code, but only what you actually did.

Tags: , , ,
Author: Xion, posted under Programming » 3 comments

printf(“R.I.P.”);

2011-11-01 20:16

Branża IT oraz nauka zwana informatyką liczą sobie już ładnych kilkadziesiąt i wygląda na to, że dziedzina ta zaczyna w pewnym sensie “dorastać”. W dość nieprzyjemnym sensie, muszę dodać. Jej znani pionierzy zaczynają bowiem nas opuszczać, czego dobitym dowodem jest zeszły miesiąc, gdy kilku z nich pożegnaliśmy w ciągu stosunkowo krótkiego czasu.
Ponieważ dzisiejszy dzień jest dobrą okazją do takich wspomnień, pozwolę sobie zwrócić uwagę na jednego z nich. Tego, który bez wątpienia miał największy wpływ na to, w jaki sposób programujemy komputery i jak obecnie wygląda przemysł software‘owy.

Mam tu na myśli oczywiście Dennisa Ritchie.

Wpływ Ritchiego na kształt informatyki jest trudny do przeceniania, bowiem miał on ogromny udział w początkach rozwoju dwóch jej ważnych aspektów: języków programowania i systemów operacyjnych. Jego zasługi w pierwszej z tych dziedzin wyrażają się głównie w stworzeniu języka C – prawdopodobnie najszerzej wykorzystywanego języka programowania w historii IT. Nawet jeśli sami nigdy w C nie programowaliśmy, to istnieje bardzo duża szansa, że nasz ulubiony język programowania ma z C wiele wspólnego: począwszy od bezpośredniej historii (C++, Objective-C), poprzez składnię (C#, Java, JavaScript, D, Scala, Go, itp.) aż po kluczowe narzędzia w rodzaju interpreterów napisanych w C (Python, Ruby, Perl, PHP). W rzeczywistości trudno jest wskazać język, który nie miałby czegokolwiek wspólnego z C – z ważniejszych należą do nich chyba tylko wczesne warianty Lispa (którego twórca, notabene, również zmarł w zeszłym miesiącu…). Niełatwo jest więc przesadzić, mówiąc o decydującym wpływie Ritchie’ego na kształt narzędzi używanych obecnie przez programistów na całym świecie.

Podobnie jest zresztą z oprogramowaniem w ogóle. System UNIX – którego Ritchie był jednym z kluczowych twórców – w niezliczonych odmianach i pochodnych działa na sporej części istniejących komputerów i wyrobów komputeropodobnych. Dotyczy to zarówno superkomputerów, wielkich serwerowni (żeby nie użyć słowa na ‘ch’ ;]) oraz małych serwerów, ale też domowych PC-tów i laptopów, a nawet urządzeń mobilnych: telefonów i tabletów. Większość (albo przynajmniej duża część) z nich operuje pod kontrolą systemów wywodzących się z UNIX-a i używa przynajmniej części związanego z nim stosu oprogramowania, którego prawdopodobnie najważniejszym komponentem jest… dokładnie tak – kompilator C.

Nie ma oczywiście żadnej pojedynczej osoby, której moglibyśmy zawdzięczać obecną postać technologii obliczeniowych i informacyjnych. Jednak Dennis Ritchie jest bez wątpienia człowiekiem, bez którego wyglądałaby ona dzisiaj zupełnie inaczej. Dlatego też warto o nim pamiętać – nawet jeśli wskaźniki z C czy uniksowy terminal są dla nas strasznymi rzeczami, z którymi nie chcemy mieć nic wspólnego :)

Tags: , ,
Author: Xion, posted under Computer Science & IT, Thoughts » 3 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.