Jak scalić i potokować wyniki z dwóch różnych poleceń do pojedynczego polecenia?

35

Chcę scalić dane wyjściowe z dwóch różnych poleceń i połączyć je w jedno polecenie.

Głupi przykład:

Polecenia, które chcę scalić dane wyjściowe:

cat wordlist.txt
ls ~/folder/*

w:

wc -l

W tym przykładzie, jeśli wordlist.txt zawiera 5 linii i 3 pliki, chcę wc -lzwrócić 8.

$cat wordlist.txt *[magical union thing]* ls ~/folder/* | wc -l
8

W jaki sposób mogę to zrobić?

David Oneill
źródło

Odpowiedzi:

56

Twoje magiczne połączenie to średnik ... i nawiasy klamrowe:

    { cat wordlist.txt ; ls ~/folder/* ; } | wc -l

Nawiasy klamrowe grupują tylko polecenia, dzięki czemu znak potoku |wpływa na łączny wynik.

Możesz także użyć nawiasów ()wokół grupy poleceń, które wykonałyby polecenia w podpowłoce. Jest to subtelny zestaw różnic z nawiasami klamrowymi, np. Wypróbuj następujące czynności:

    cd $HOME/Desktop ; (cd $HOME ; pwd) ; pwd
    cd $HOME/Desktop ; { cd $HOME ; pwd ; } ; pwd

Zobaczysz, że wszystkie zmienne środowiskowe, w tym bieżący katalog roboczy, są resetowane po wyjściu z grupy nawiasów, ale nie po wyjściu z grupy nawiasów klamrowych.

Jeśli chodzi o średnik, alternatywami są znaki &&i ||, które warunkowo wykonają drugie polecenie tylko wtedy, gdy pierwsze zakończy się powodzeniem lub jeśli nie, np.

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."
pablomme
źródło
1
To działa! Pomóż mi się nauczyć - co dokładnie robią tutaj nawiasy klamrowe? Jakie inne magiczne moce mają?
David Oneill,
@DavidOneill { list; }to polecenie złożone . Od bash(1): lista jest po prostu wykonywana w bieżącym środowisku powłoki. lista musi być zakończona znakiem nowej linii lub średnikiem. [..] Status powrotu to status wyjścia z listy.
Lekensteyn,
Dodałem nieco więcej informacji do odpowiedzi. Ostatecznym przewodnikiem po skryptach powłoki za pomocą bash jest strona podręcznika bash, wpisz man bashz linii poleceń i przejrzyj ją.
pablomme
powinien; w tych poleceniach nie jest && w przypadku, gdy plik wordlist.txt nie istnieje?
Rinzwind
Jeśli wordlist.txtnie istnieje, błąd catpojawi się na standardowym błędzie, ale nie na standardowym wyjściu, więc wc -lnie będzie zliczał żadnych wierszy z niego. To samo dotyczy ~/foldernieistnienia. Można dodać 2> /dev/nullmiędzy każdą z tych komend a ich średnikiem, aby zapobiec zakłóceniom przy standardowym błędzie, ale poza brzydkimi komunikatami o błędach są nieszkodliwe dla zliczania linii.
pablomme
8

Ponieważ wcakceptuje ścieżkę pliku jako dane wejściowe, możesz również użyć podstawienia procesu:

wc -l <(cat wordlist.txt; ls ~/folder/*)

Odpowiada to mniej więcej:

echo wordlist.txt > temp
ls ~/folder/* >> temp
wc -l temp

Pamiętaj, że ls ~/folder/*zwraca również zawartość podkatalogów, jeśli takie istnieją (z powodu globalnej ekspansji). Jeśli chcesz tylko wyświetlić zawartość ~/folder, po prostu użyj ls ~/folder.

Lekensteyn
źródło
To wc -lbył tylko wymyślony przykład, w rzeczywistości instaluję go w coś bardziej skomplikowanego, który nie ma tej opcji.
David Oneill,
2
@DavidOneill Cóż, ponieważ catakceptuje argument pliku, możesz użyć, cat <(cat wordlist.txt; ls wordlist.txt) | wc -la nawet cat wordlist.txt <(ls wordlist.txt) | wc -l. Jest to oczywiście bardzo brzydkie, ale pokazuje nieskończone możliwości dzięki narzędziom wiersza poleceń.
Lekensteyn,
1
@DavidOneill Prawidłowo, poprawiłem to teraz, dzięki.
Lekensteyn,
1
Chcesz ls ~/folder/, aby if ~/folderbył dowiązaniem symbolicznym lswyświetla zawartość jego celu zamiast samego łącza. Nawiasem mówiąc, po wszystkich lspoleceniach powinno następować -1tak, aby drukowany był jeden plik na linię i wc -lbył to prawidłowy sposób zliczania plików.
pablomme
@pablomme Masz rację co do dowiązania symbolicznego, ale -1sugeruje się, że wyjście nie jest terminalem, ale potokiem.
Lekensteyn,
2

Zadawałem sobie to samo pytanie i ostatecznie napisałem krótki scenariusz.

magicalUnionThing(Nazywam to append):

#!/bin/sh
cat /dev/stdin
$*

Spraw, aby ten skrypt był wykonywalny

chmod +x ./magicalUnionThing

Teraz ty

cat wordlist.txt |./magicalUnionThing ls ~/folder/* | wc -l

Co to robi:

  • Wyślij standardowe wejście na standardowe wyjście
  • Wykonaj argument. $*zwraca wszystkie argumenty jako ciąg. Wyjście tej komendy domyślnie przechodzi na standardowe wyjście skryptu.

Stdout magicalUnionThing będzie więc stdin + stdout polecenia przekazanego jako argument.

Istnieją oczywiście prostsze sposoby, jak w przypadku innych odpowiedzi.
Być może ta alternatywa może być przydatna w niektórych przypadkach.

Rolf
źródło