Как объединить и передать результаты двух разных команд в одну команду?

35

Я хочу объединить (объединить) вывод из двух разных команд и передать их в одну команду.

Глупый пример:

Команды, которые я хочу объединить вывод:

cat wordlist.txt
ls ~/folder/*

в:

wc -l

В этом примере, если wordlist.txt содержит 5 строк и 3 файла, я хочу wc -lвернуть 8.

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

Как я могу это сделать?

Дэвид Онеилл
источник

Ответы:

56

Ваш магический союз - точка с запятой ... и фигурные скобки:

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

В фигурных скобках команды сгруппированы только вместе, поэтому знак канала |влияет на объединенный вывод.

Вы также можете использовать круглые скобки ()вокруг группы команд, которые будут выполнять команды в подоболочке. Это имеет тонкий набор различий с фигурными скобками, например, попробуйте следующее:

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

Вы увидите, что все переменные окружения, включая текущий рабочий каталог, сбрасываются после выхода из группы в скобках, но не после выхода из группы фигурных скобок.

Что касается точки с запятой, альтернативы включают &&и ||знаки, которые условно выполнить вторую команду , только если первая успешна или нет, соответственно, например ,

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."
pablomme
источник
1
Это работает! Помогите мне узнать - что именно здесь делают фигурные скобки? Какие еще магические силы они имеют?
Дэвид Онеил
@DavidOneill { list; }- составная команда . От bash(1): список просто выполняется в текущей среде оболочки. список должен заканчиваться символом новой строки или точкой с запятой. [..] Статус возврата - это статус выхода из списка.
Лекенштейн
Я добавил немного больше информации к ответу. Полное руководство по написанию сценариев оболочки с помощью bash - это man-страница bash, введите ее man bashиз командной строки и просмотрите ее.
Паблом
должен; в этих командах не должно быть && в случае, если файл wordlist.txt не существует?
Rinzwind
Если wordlist.txtне существует, ошибка от catпоявится при стандартной ошибке, но не при стандартном выводе, поэтому wc -lне будет отсчитывать ни одной строки из нее. То же самое касается ~/folderне существующего. Можно было бы добавить 2> /dev/nullмежду каждой из этих команд и их точкой с запятой, чтобы предотвратить шум при стандартной ошибке, но, помимо того, что это некрасиво, сообщения об ошибках безвредны для подсчета строк.
Паблом
8

Поскольку wcв качестве входных данных принимается путь к файлу, вы также можете использовать подстановку процесса:

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

Это примерно эквивалентно:

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

Имейте в виду, что ls ~/folder/*также возвращает содержимое подкаталогов, если таковые имеются (из-за расширения glob). Если вы просто хотите перечислить содержимое ~/folder, просто используйте ls ~/folder.

Lekensteyn
источник
Это wc -lбыл просто выдуманный пример, я на самом деле собираюсь сделать что-то более сложное, у которого нет этой опции.
Дэвид Онеил
2
@DavidOneill Ну, так как catпринимает аргумент файла, вы можете использовать cat <(cat wordlist.txt; ls wordlist.txt) | wc -lи даже cat wordlist.txt <(ls wordlist.txt) | wc -l. Это, конечно, очень уродливо, но демонстрирует бесконечные возможности с инструментами командной строки.
Лекенштейн
1
@DavidOneill Правильно, я исправил это сейчас, спасибо.
Лекенштейн
1
Вы хотите, ls ~/folder/чтобы, если ~/folderэто символическая ссылка, lsперечисляла содержимое своей цели вместо самой ссылки. Между прочим, все lsкоманды должны сопровождаться -1так, чтобы один файл на строку печатался и wc -lбыл правильным способом подсчета файлов.
Паблом
@pablomme Вы правы насчет символической ссылки, но -1подразумевается, что вывод - это не терминал, а канал.
Лекенштейн
2

Я задавал себе тот же вопрос и в итоге написал короткий сценарий.

magicalUnionThing(Я это называю append):

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

Сделайте этот скрипт исполняемым

chmod +x ./magicalUnionThing

Теперь ты

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

Что оно делает:

  • Отправить стандартный ввод на стандартный вывод
  • Выполнить аргумент. $*возвращает все аргументы в виде строки. Вывод этой команды идет в стандартный вывод скрипта по умолчанию.

Таким образом, стандартный вывод magicalUnionThing будет его стандартным выводом + стандартный вывод команды, передаваемой в качестве аргумента.

Есть, конечно, более простые способы, как в других ответах.
Может быть, эта альтернатива может быть полезна в некоторых случаях.

Рольф
источник