2013-05-10 22 views
6

Próbuję przekazać informacje do programu, który nie przyjmuje danych wejściowych ze standardowego wejścia. Aby to zrobić, używam/dev/stdin jako argumentu, a następnie próbuję potokować w moim pliku wejściowym. Zauważyłem, że jeśli robię to z postacią rury:stdin zachowuje się inaczej po instalacji i przekierowaniu

[[email protected] ernwin]$ cat fess/structures/168d.pdb | MC-Annotate /dev/stdin 

Nie otrzymuję danych wyjściowych. Jeśli jednak zrobić to samo przy użyciu lewy znak daszka, to działa dobrze:

[[email protected] ernwin]$ MC-Annotate /dev/stdin < fess/structures/168d.pdb 
Residue conformations ------------------------------------------- 
A1 : G C3p_endo anti 
A2 : C C3p_endo anti 
A3 : G C3p_endo anti 

Moje pytanie brzmi, jaka jest różnica między tymi dwoma procesami i dlaczego dać inny wynik? Jako pytanie dodatkowe, czy istnieje właściwe określenie wprowadzania danych za pomocą symbolu "<"?

Aktualizacja:

Mój obecny najlepszy Domyślam się, że coś się wewnątrz istoty przebiegu programu wykorzystuje szukając w pliku. Poniższe odpowiedzi zdają się sugerować, że ma coś wspólnego ze wskaźnikami ale plik uruchamiając następujące mały program testowy:

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    FILE *f = fopen(argv[1], "r"); 
    char line[128]; 

    printf("argv[1]: %s f: %d\n", argv[1], fileno(f)); 

    while (fgets(line, sizeof(line), f)) { 
    printf("line: %s\n", line); 
    } 

    printf("rewinding\n"); 
    fseek(f, 0, SEEK_SET); 

    while (fgets(line, sizeof(line), f)) { 
    printf("line: %s\n", line); 
    } 
    fclose(f); 
} 

wskazuje, że wszystko odbywa się identycznie aż do wywołania fseek funkcji:

[[email protected] tmp]$ cat temp | ./a.out /dev/stdin 
argv[1]: /dev/stdin f: 3 
line: abcd 

rewinding 
=================== 
[[email protected] tmp]$ ./a.out /dev/stdin < temp 
argv[1]: /dev/stdin f: 3 
line: abcd 

rewinding 
line: abcd 

Korzystanie z podstawienia procesu, jak Christopher Neylan sugeruje, że prowadzi do programu powyżej wiszącego nawet bez czytania wejścia, co również wydaje się trochę dziwne.

[[email protected] tmp]$ ./a.out /dev/stdin <(cat temp) 
argv[1]: /dev/stdin f: 3 

Patrząc na wyjście strace potwierdza moje podejrzenie, że operacja poszukiwania jest usiłowanie, które nie w wersji rurze:

_llseek(3, 0, 0xffffffffffd7c7c0, SEEK_CUR) = -1 ESPIPE (Illegal seek) 

I powiedzie w wersji przekierowania.

_llseek(3, 0, [0], SEEK_CUR)   = 0 

Morał z historii: nie przypadkowo próbować zastąpić argument z /dev/stdin i starają się rury do niego. To może zadziałać, ale równie dobrze może nie.

+0

Sprawdź informacje tutaj: http://stackoverflow.com/questions/1312922/detect-if-stdin-is-a-terminal-or-pipe-in-cc-qt – Atle

+0

To interesujące, ale wydaje się, że nie robi takiego czeku. I niezależnie od tego, czy wyjście z isatty() nie powinno być takie samo w obu wymienionych tutaj przypadkach wejściowych? Jest to przeciwieństwo do połączonego wpisu, który nie ma przekierowanych danych wejściowych. –

+0

Zgodnie z drugą odpowiedzią | zwraca TAK dla isatty while Atle

Odpowiedz

0

od patrzenia na te informacje o MC-opisywanie http://bioinfo.cipf.es/ddufour/doku.php?id=mc-annotate tego powodu, że rura nie działa dlatego, MC-Annotate nie rozpoznaje cat wyjście z pliku jako jednego typu .pbd

łańcuchy rur poleceń razem wyjście pierwszego jest używany jako wejście do następnego.

"<" ("mniej niż", "strzałka w lewo", "nawias trójkątny") wprowadza plik do polecenia.

http://tldp.org/LDP/abs/html/io-redirection.html#IOREDIRECTIONREF2

+0

Dlaczego miałoby to być sprawa jednak? Plik pdb to tylko plik tekstowy. Jeśli wykonasz następujące polecenie: catout.pdb> out1.pdb; diff out.pdb out1.pdb', nie ma różnicy, co prowadzi mnie do przekonania, że ​​wynik polecenia 'cat' jest identyczny z oryginalnym plikiem. –

+0

Ponieważ wyjście kota nie jest plikiem pdb. Patrząc na dokumentację dla MC-Adnotate, dostępna jest opcja '-b' do odczytywania pliku binarnego zamiast pdb. Zgaduję, że logika dla typu pliku jest wewnętrzna dla MC-Adnotate. – Schleis

+0

Myślę, że prawdopodobnie masz rację, ponieważ nie widzę podobnych zachowań z innymi programami. Nadal nie mogę jednak zrozumieć, dlaczego plik i wejście przewodowe powinny być traktowane inaczej, jeśli są dokładnie takie same pod względem zawartości. –

0

Problem tkwi w kolejności, w której pliki są otwarte do czytania.

nie jest prawdziwym plikiem; jest to dowiązanie symboliczne do pliku, którego bieżący proces używa jako standardowego wejścia. W typowej powłoce jest połączony z terminalem i dziedziczony przez dowolny proces rozpoczęty przez powłokę. Pamiętaj, że MC-Annotate odczytuje tylko z pliku dostarczonego jako argument.

W przykładzie potoku, /dev/stdin jest dowiązaniem symbolicznym do pliku, który MC-Annotate dziedziczy jako standardowe wejście: terminal. Prawdopodobnie otwiera ten plik na nowym deskryptorze (powiedzmy 3, ale może to być dowolna wartość większa niż 2). Rura łączy dane wyjściowe ze standardowego wejścia cat do MC-Annotate's (deskryptor pliku 0), który MC-Annotate nadal ignoruje na rzecz pliku, który został bezpośrednio otwarty.

W przykładzie przekierowania, powłoka łączy fess/structures/168d.pdb bezpośrednio do pliku deskryptora 0 przedMC-Annotate prowadzony jest. Po uruchomieniu MC-Annotate ponownie próbuje otworzyć /dev/stdin, który tym razem wskazuje na fess/structures/168d.pdb zamiast terminala.

Więc odpowiedź leży w tym, w którym pliku /dev/stdin jest link do procesu, który wykonuje MC-Annotate; przekierowania powłoki są konfigurowane przed proces rozpoczyna się; pipeline po proces się rozpoczyna.

Czy to działa?

cat fess/structures/168d.pdb | MC-Annotate <(cat /dev/stdin) 

podobne polecenie

echo foo | cat <(cat /dev/stdin) 

wydaje się działać, ale nie będę twierdzą sytuacje są identyczne.


[AKTUALIZACJA: nie działa. /dev/stdin jest nadal łączem do terminala, a nie do potoku.]

To może zapewnić obejście. Teraz, MC-Annotate dziedziczy standardowe wejście z podpowłoki, a nie z bieżącej powłoki, a podpowłoką jest wyjście cat jako standardowe wejście, a nie terminal.

cat fess/structures/168d.pdb | (MC-Annotate /dev/stdin) 

To myśleć prosty grupa komenda zadziała także:

cat fess/structures/168d.pdb | { MC-Annotate /dev/stdin; } 
+0

Wow! Świetny opis. W jaki sposób można go oszukać, nie otwierając nowego deskryptora pliku podczas odbierania potokowego wejścia? –

+0

Zobacz moją aktualizację. "MC-Adnotate" nadal będzie otwierać nowy deskryptor pliku; Nie sądzę, że program wykonuje jakiekolwiek wewnętrzne wykrywanie: po prostu otwiera plik prezentowany w linii poleceń. Jednak moja aktualizacja pokazuje sposób uruchomienia 'MC-Adnotate' w środowisku, w którym dziedziczy on coś innego niż terminal jako standardowe wejście. – chepner

+0

Hmm ... żadne z obejść nie działało. –

1

Nie powinno być funkcjonalna różnica pomiędzy tymi dwoma poleceniami. Rzeczywiście, nie mogę odtworzyć tego, co widzisz:

#! /usr/bin/perl 
# test.pl 
# this is a test Perl script that will read from a filename passed on the command line, and print what it reads. 

use strict; 
use warnings; 

print $ARGV[0], " -> ", readlink($ARGV[0]), " -> ", readlink(readlink($ARGV[0])), "\n"; 
open(my $fh, "<", $ARGV[0]) or die "$!"; 
while(defined(my $line = <$fh>)){ 
     print "READ: $line"; 
} 
close($fh); 

Running to trzy sposoby:

([email protected]: tmp)$ cat input 
a 
b 
c 
d 

([email protected]: tmp)$ ./test.pl /dev/stdin 
/dev/stdin -> /proc/self/fd/0 -> /dev/pts/0 
this is me typing into the terminal 
READ: this is me typing into the terminal 

([email protected]: tmp)$ cat input | ./test.pl /dev/stdin 
/dev/stdin -> /proc/self/fd/0 -> pipe:[1708285] 
READ: a 
READ: b 
READ: c 
READ: d 

([email protected]: tmp)$ ./test.pl /dev/stdin < input 
/dev/stdin -> /proc/self/fd/0 -> /tmp/input 
READ: a 
READ: b 
READ: c 
READ: d 

Pierwsza uwaga co /dev/stdin jest:

([email protected]: tmp)$ ls -l /dev/stdin 
lrwxrwxrwx 1 root root 15 Apr 21 15:39 /dev/stdin -> /proc/self/fd/0 

([email protected]: tmp)$ ls -l /proc/self 
lrwxrwxrwx 1 root root 0 May 10 09:44 /proc/self -> 27565 

To zawsze dowiązaniem do /proc/self/fd/0 . /proc/self sam jest specjalnym łączem do katalogu pod numerem /proc dla bieżącego procesu. Tak więc /dev/stdin zawsze wskaże fd 0 bieżącego procesu. Więc kiedy uruchomisz MC-Annotate (lub, w moich przykładach, test.pl), plik /dev/stdin rozwiąże się na /proc/$pid/fd/0, niezależnie od identyfikatora procesu MC-Annotate. Jest to po prostu efekt działania dowiązania symbolicznego do /dev/stdin.

Więc jak widać powyżej w moim przykładzie, w przypadku korzystania z potoku (|) /proc/self/fd/0 skieruje do odczytu końca rury z cat utworzonej przez powłokę. Podczas korzystania z przekierowania (<), /proc/self/fd/0 będzie wskazywać bezpośrednio na plik wejściowy, skonfigurowany przez powłokę.

Co do tego, dlaczego widzisz dziwne zachowanie - domyślam się, że MC-Annotate sprawdza niektóre typy plików przed ich otwarciem i pokazuje, że/dev/stdin wskazuje na nazwaną potok zamiast zwykłego plik i jest ratowany. Możesz to potwierdzić, czytając kod źródłowy dla MC-Annotate lub używając polecenia strace, aby obejrzeć, co dzieje się wewnętrznie.

Należy zauważyć, że obie te metody są nieco zaokrąglone w Bash. Przyjęty sposób, aby uzyskać wyjście procesu do programu, które zostanie otwarte tylko nazwę pliku jest użycie process substitution:

$ MC-Annotate <(cat fess/structures/168d.pdb) 

<(...) konstrukt zwraca deskryptor pliku do odczytu końca rury, która nadchodzi z cokolwiek ... jest:

([email protected]: tmp)$ echo <(true | grep example | cat) 
/dev/fd/63 
Powiązane problemy