Przenośny sposób na znalezienie numeru i-węzła

10

Początkowo korzystałem stat -c %i file(aby wykryć obecność więzienia ), który zdawał się działać na dowolnej dystrybucji Linuksa pod słońcem. Na OS X 'musiałem użyć ls -i file | cut -d ' ' -f 1.

Czy jest jakiś sposób na znalezienie numeru i-węzła pliku w skrypcie powłoki, który jest przenośny na platformach * nix i nie zależy od notorycznie kapryśnych ls?

l0b0
źródło
1
Możesz być zainteresowany lub mieć lepsze odpowiedzi na pytanie: Jak mam powiedzieć, że biegam w chroot? .
Gilles „SO- przestań być zły”
Czy możesz rozwinąć temat „notorycznie kapryśnej ls”?
jlliagre
@jlliagre: Inni już to zrobili lepiej.
l0b0,
Okej, dla takich plików zobacz moją odpowiedź.
jlliagre

Odpowiedzi:

11

Możliwe rozwiązanie: specyfikacja POSIX dlals specyfikacji -i, więc może jest przenośna. Czy ktoś wie o popularnej implementacji, lsktóra tego nie obsługuje, lub drukuje ją w inny sposób niż w poniższym przykładzie:

$ ls -di /
2 /
l0b0
źródło
3
@jlliagre: Proszę przeczytać przed wysłaniem. statKomenda nie działa na OS X, ls -dipracował na obu.
l0b0,
1
Nawet Busybox lsma -di -ijako obowiązkowe funkcje (chociaż lssam jest opcjonalny, jak wszystko inne).
Gilles „SO- przestań być zły”
1
Nieporozumienie Michaela było dokładnie tym, co komentowałem. Nie jest wart dość niegrzecznego i niezasłużonego komentarza „czytaj przed opublikowaniem”.
jlliagre
2
Tam wyjątki od tej reguły: lsz -iprzednich-klocki ze spacjami na co najmniej 10 (ewentualnie Solaris Solaris 11, nie mam zaznaczone). Wygląda na to, że było to tradycyjne zachowanie wracające do Uniksa w wersji 7, więc podejrzewam, że wiele korporacyjnych smaków * nix zachowało to zachowanie (ale mam tylko Solaris 10 pod ręką). Tak blisko, jak mogę powiedzieć, jeśli użyjesz czegoś, co poprawnie wyznacza pola w dowolnym spacji (więc nie cut, ale na przykład awklub po prostu własne dzielenie pól powłoki), przenośne jest oczekiwanie, że pierwszy ciąg niebiałych znaków będzie i-węzłem numer.
mtraceur
1
@ l0b0 Tak. Wymaga masochistycznego poświęcenia: szeregu badań / testów i zapamiętywania dla stale malejących zysków. Jest to możliwe, przynajmniej dla niektórych definicji „przenośnych”, ale nie jest to przyjemne doświadczenie.
mtraceur
2

Powinno to być przenośne i współpracować z nazwami plików zawierającymi spacje, znaki nowej linii lub inne dziwne znaki, prowadzące do notorycznie kapryśnego zachowania ls.

filename="whatever file name"
find . -name "$filename" -exec sh -c 'ls -di "$0" | head -1' {} \;
jlliagre
źródło
1

Aby zwiększyć przenośność, możesz także zaimplementować specyficzną dla platformy funkcję otoki (tutaj nazywaną statinode()) wokół statpolecenia, która może być oparta na danych wyjściowych uname -s(zobacz uname ).

ls byłoby potrzebne tylko jako opcja rezerwowa.

(
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
)
Jeff
źródło
0

statjest częścią pakietu GNU Coreutils . OSX używa innej statimplementacji (prawdopodobnie opartej na BSD), która nie przyjmuje tych samych argumentów wiersza poleceń.

Zawsze możesz zainstalować GNU Coreutils na OSX. Oczywiście to nie pomaga, jeśli potrzebujesz rozwiązania, które działa na systemach OSX, które nie mają GNU Coreutils.

Lub, jeśli poprawnie czytam stronę podręcznika stat OSX stat (1) , stat -f %i filew OSX zachowuje się jak stat -c %i fileprzy użyciu wersji Coreutils. (Określenie, którą wersję statposiadasz, to inna sprawa; możesz spróbować stat --version >/dev/null; jeśli się powiedzie, masz wersję GNU Coreutils).

ls -diRozwiązanie jest bardziej mobilny i mniej kłopotów, ale jest to alternatywa.

Keith Thompson
źródło
0

Inne rozwiązanie:

#!/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";

Prawdopodobnie możesz bezpiecznie założyć, że Perl jest zainstalowany.

Keith Thompson
źródło
0

Podobnie do podejścia Jeffa, statmożna go również przetestować bezpośrednio.

(
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
)
ianc
źródło