Uczyłem się o linii poleceń i dowiedziałem się, że |
(potok) ma przekierowywać dane wyjściowe z polecenia do wejścia innego. Dlaczego więc to polecenie ls | file
nie działa?
file
input jest jedną z wielu nazw plików, takich jak file filename1 filename2
ls
wyjście to lista katalogów i plików w folderze, więc pomyślałem, że ls | file
powinien pokazać typ pliku każdego pliku w folderze.
Jednak gdy go używam, dane wyjściowe są następujące:
Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
[-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
file -C [-m magicfiles]
file [--help]
Ponieważ wystąpił błąd podczas używania file
polecenia
command-line
ls
pipe
file-command
IanC
źródło
źródło
ls
, oznacza to, że chcesz, aby wszystkie pliki w bieżącym katalogu były obsługiwane za pomocąfile
polecenia. ... Dlaczego więc nie po prostu zrobić:,file *
który odpowie linią dla każdego pliku, folderu.file *
to najmądrzejszy sposób, po prostu zastanawiałem się, dlaczego użyciels
wyjścia nie działa. Wątpliwości wyjaśnione :)ls
jest generalnie złym pomysłem.Odpowiedzi:
Podstawową kwestią jest to, że
file
oczekuje się , że nazwy plików będą argumentami wiersza poleceń, a nie standardowego wejścia. Kiedy piszesz,ls | file
dane wyjściowels
są przekazywane jako dane wejściowe dofile
. Nie jako argumenty, jako dane wejściowe.Co za różnica?
Argumenty wiersza poleceń występują podczas pisania flag i nazw plików po poleceniu, jak w
cmd arg1 arg2 arg3
. W skryptach powłoki te argumenty są dostępne jako zmienne$1
,$2
,$3
, itd. W C wy mieliście do nich dostęp za pośrednictwemchar **argv
iint argc
argumentymain()
.Standardowe wejście, stdin, to strumień danych. Niektóre programy podoba
cat
lubwc
odczytać z stdin gdy oni nie podano żadnych argumentów wiersza polecenia. W skrypcie powłoki można użyćread
jednego wiersza danych wejściowych. W C możesz użyćscanf()
lubgetchar()
spośród różnych opcji.file
zwykle nie odczytuje ze standardowego wejścia. Oczekuje, że co najmniej jedna nazwa pliku zostanie przekazana jako argument. Dlatego wypisuje użycie podczas pisanials | file
, ponieważ nie przekazałeś argumentu.Możesz użyć
xargs
do konwersji standardowego wejścia na argumenty, jak wls | xargs file
. Jednak, jak wspomina terdon , parsowaniels
jest złym pomysłem. Najbardziej bezpośrednim sposobem na to jest po prostu:źródło
file
pobranie nazw plików z jego danych wejściowych, używającls | file -f -
. Nadal zły pomysł na c.ls
wyjście rur dofile
standardowego wejścia. Wypróbuj to.file $(ls)
, co również działa, w jeszcze inny sposób.Ponieważ, jak mówisz, wejściem
file
muszą być nazwy plików . Wynikiem jestls
jednak tylko tekst. To, że jest to lista nazw plików, nie zmienia faktu, że jest to po prostu tekst, a nie lokalizacja plików na dysku twardym.Gdy zobaczysz wydruk wydrukowany na ekranie, zobaczysz tekst. To, czy ten tekst jest wierszem, czy listą nazw plików, nie ma znaczenia dla komputera. Wie tylko, że to tekst. Dlatego możesz przekazać dane wyjściowe
ls
do programów, które przyjmują tekst jako dane wejściowe (chociaż tak naprawdę nie powinieneś ):Tak więc, aby użyć danych wyjściowych polecenia, które wyświetla nazwy plików jako tekst (np.
ls
Lubfind
) jako dane wejściowe dla polecenia, które przyjmuje nazwy plików, musisz użyć kilku sztuczek. Typowym narzędziem do tego jestxargs
:Jak już powiedziałem wcześniej, naprawdę nie chcesz analizować wyników
ls
. Coś takiegofind
jest lepsze (print0
wypisuje\0
zamiast newilne po każdej nazwie pliku, a-0
ofxargs
pozwala na radzenie sobie z takimi danymi wejściowymi; jest to sztuczka, aby twoje polecenia działały z nazwami plików zawierającymi nowe linie):Który ma też swój własny sposób na to, bez potrzeby
xargs
:Na koniec możesz także użyć pętli powłoki. Należy jednak pamiętać, że w większości przypadków
xargs
będzie znacznie szybszy i bardziej wydajny. Na przykład:źródło
file
nie wydaje się właściwie odczytać stdin chyba że podano wyraźne-
zastępczy: porównaniefile foo
,echo foo | file
orazecho foo | file -
; w rzeczywistości jest to prawdopodobnie powód komunikatu o użyciu w przypadku OP (tzn. nie jest tak naprawdę dlatego, że wynikiemls
jest „po prostu tekst”, ale raczej dlatego, że lista argumentówfile
jest pusta)echo foo | file -
tak naprawdę nie działafile
na pliku,foo
ale na strumieniu standardowym.cat
wyjątkiem stdin bez wyjątku,-
gdy wydaje mi się, że podane są argumenty pliku?Nie „przekierowuje” danych wyjściowych, ale pobiera dane wyjściowe programu i używa ich jako danych wejściowych, podczas gdy plik nie przyjmuje danych wejściowych, ale nazwy plików jako argumenty , które są następnie testowane. Przekierowania nie przekazują tych nazw plików jako argumentów ani potoków, ani później, co robisz.
Co możesz zrobić, to odczytać nazwy plików z pliku z
--files-from
opcją, jeśli masz plik z listą wszystkich plików, które chcesz przetestować, w przeciwnym razie po prostu przekaż ścieżki do swoich plików jako argumenty.źródło
Przyjęta odpowiedź wyjaśnia, dlaczego polecenie potoku nie działa od razu, a wraz z
file *
poleceniem oferuje proste, proste rozwiązanie.Chciałbym zasugerować inną alternatywę, która może się kiedyś przydać. Sztuczka polega na użyciu
(`)
znaku wstecznego . Wstecz został szczegółowo wyjaśniony tutaj . W skrócie, pobiera dane wyjściowe polecenia zawartego w backticks i zastępuje je jako ciąg znaków w pozostałym poleceniu.Tak więc
find `ls`
weźmie wynikls
polecenia i zastąpi go argumentemfind
polecenia. Jest to dłuższe i bardziej skomplikowane niż przyjęte rozwiązanie, ale jego warianty mogą być pomocne w innych sytuacjach.źródło
command
(nie mogę znaleźć kodu ukośnika odwrotnego na moim telefonie), aby rozwinąć wyjście polecenia w bashu i użyć go jako parametru do innych poleceń. Naprawdę przydatne, nawet jeśli użycie go w tym przypadku (z ls ) nadal spowoduje pewne problemy ze względu na znaki specjalne w niektórych nazwach plików.Wyjście
ls
przez potok jest solidnym blokiem danych z 0x0a oddzielającymi każdą linię - tj. Znakiem wysuwu linii - ifile
otrzymuje to jako jeden parametr, w którym oczekuje się, że wiele znaków będzie działać na jednym na raz.Zgodnie z ogólną zasadą nigdy nie używaj
ls
do generowania źródła danych dla innych poleceń - pewnego dnia przepłynie on do…rm
i wtedy będziesz miał kłopoty!Lepiej użyć pętli, na przykład,
for i in *; do file "$i" ; done
która da oczekiwany wynik. Cytaty są dostępne w przypadku nazw plików ze spacjami.źródło
file *
;-)ls
jest bardzo, bardzo złym pomysłem . Nie tylko dlatego, że możesz przekazać go do czegoś szkodliwego, na przykładrm
, co ważniejsze, ponieważ psuje się każdą niestandardową nazwę pliku.rm
pobiera nazwy plików ze standardowego wejścia? Myślę, że nie. Ponadto, jako ogólna zasada,ls
jest jednym z głównych przykładów źródła danych do korzystania z potoków uniksowych od początku jego istnienia. Dlatego domyślnie jest to prosta jedna nazwa pliku na wiersz bez atrybutów i ozdób, gdy jego wyjściem jest potok, w przeciwieństwie do zwykłego domyślnego formatowania, gdy wyjściem jest terminal.Jeśli chcesz użyć potoku do karmienia,
file
użyj opcji, po-f
której zwykle następuje nazwa pliku, ale możesz także użyć pojedynczego łącznika-
do odczytu ze standardowego wejścia, więcSztuczka z łącznikiem
-
działa z wieloma standardowymi narzędziami wiersza poleceń (chociaż--
czasami), więc zawsze warto spróbować.Narzędzie
xarg
jest znacznie potężniejsze i w większości przypadków potrzebne tylko, jeśli lista argumentów jest zbyt długa (zobacz ten post, aby uzyskać szczegółowe informacje).źródło
--
? Nigdy tego nie widziałem.--
jest zwykle wskaźnikiem „końca flag”.Działa użyj polecenia jak poniżej
Będzie dla mnie lepiej
źródło
Powinno to również działać:
jak również omówione tutaj: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff
źródło
$()
Sposób jest taki sam jak sposób grawis w askubuntu.com/a/795744/158442