Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

At first I used stat -c %i file (to help detect the presence of a jail), which seemed to work on any Linux distribution under the sun. On OS X' I had to use ls -i file | cut -d ' ' -f 1.

Is there some way to find the inode number of a file in a shell script which is portable across *nix platforms and does not depend on the notoriously capricious ls?

share|improve this question
1  
You may be interested in, or have better answers for, How do I tell I'm running in a chroot?. –  Gilles Nov 8 '11 at 23:56
    
Can you elaborate about the "notoriously capricious ls" ? –  jlliagre Nov 10 '11 at 0:34
    
@jlliagre: Others have done it better already. –  l0b0 Nov 10 '11 at 13:32
    
Okay, for such files, see my reply. –  jlliagre Nov 11 '11 at 14:46
add comment

6 Answers 6

up vote 7 down vote accepted

Possible solution: The POSIX spec for ls specifies -i, so maybe it's portable. Does anyone know of a popular implementation of ls which does not support this, or prints it in a different way from the following example:

$ ls -di /
2 /
share|improve this answer
    
@greenmang0 He mentioned in the question that it didn't work on OSX –  Michael Mrozek Nov 8 '11 at 14:07
    
ls -di should just work on OSX just like it should (and likely does) on every Unix/Unix like OS. –  jlliagre Nov 8 '11 at 14:51
3  
@jlliagre: Please read before posting. The stat command didn't work on OS X, ls -di worked on both. –  l0b0 Nov 8 '11 at 14:54
1  
Even Busybox ls has -d and -i as mandatory features (though ls itself is optional, like everything else). –  Gilles Nov 9 '11 at 0:00
    
@l0b0: I don't get your reply. I was commenting on Michael Mrozek own comment which states ls -di didn't work unless I'm misunderstanding it. –  jlliagre Nov 9 '11 at 12:06
show 2 more comments

stat is part of the GNU Coreutils package. OSX uses a different stat implementation (presumably a BSD-based one) which doesn't take the same command-line arguments.

You could always install GNU Coreutils on OSX. Of course that doesn't help if you need a solution that works on OSX systems that don't have GNU Coreutils.

Or, if I'm reading the OSX stat(1) man page correctly, stat -f %i file on OSX behaves like stat -c %i file using the Coreutils version. (Determining which version of stat you have is another matter; you could try stat --version >/dev/null; if it succeeds, you have the GNU Coreutils version.)

The ls -di solution is more portable and less trouble, but this is an alternative.

share|improve this answer
add comment

Another solution:

#!/usr/bin/perl

use strict;
use warnings;

die "Usage: $0 filename\n" if scalar @ARGV != 1;
my $file = $ARGV[0];
my @stat = stat $file;
die "$file: $!\n" if not @stat;
print "$stat[1]\n";

You can probably safely assume that Perl is installed.

share|improve this answer
add comment

To increase portability you may also implement a platform-specific wrapper function (here called statinode()) around the stat command that can be based on the output of uname -s (see uname).

ls would be needed as a fallback option only.

(
shopt -s nocasematch nullglob    # using Bash
case "$(uname -s)" in
   # nocasematch alternative
   #[Ll][Ii][Ni][Uu][Xx]   )  statinode() { stat -c '%i' "$@"; return 0; };;
   "Linux"   )      statinode() { stat -c '%i' "$@"; return 0; };;
   "Darwin"  )      statinode() { stat -f '%i' "$@"; return 0; };;
   "FreeBSD" )      statinode() { stat -f '%i' "$@"; return 0; };;
           * )      statinode() { ls -id "$@" | cut -d ' ' -f 1; return 0; };;
esac
#export -f statinode
statinode / / / /
shopt -u nocasematch nullglob
)
share|improve this answer
add comment

This should be portable and work with file names containing spaces, newlines or other odd characters leading to the notoriously capricious ls behavior.

filename="whatever file name"
find . -name "$filename" -exec sh -c 'ls -di "$0" | head -1' {} \;
share|improve this answer
add comment

Similar to jeff's approach, stat could be tested directly as well.

(
if (stat -c '%i' / 1>/dev/null 2>&1; exit $?); then
   statinode() { stat -c '%i' "$@"; return 0; }
elif (stat -f '%i' / 1>/dev/null 2>&1; exit $?); then
   statinode() { stat -f '%i' "$@"; return 0; }
elif test -n "$(exec 2>/dev/null; ls -id / | cut -d ' ' -f 1)"; then
   statinode() { ls -id "$@" | cut -d ' ' -f 1; return 0; }
else
   echo 'Could not create statinode(). Exiting ...' && exit 1
fi
# export -f statinode
statinode / / / /
declare -f statinode
)
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.