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

165

Я предполагал, что самый простой способ сравнить содержимое двух одинаковых каталогов будет что-то вроде

diff `ls old` `ls new`

Но я понимаю, почему это не работает; diffпередается большой длинный список файлов в командной строке, а не два потока, как я надеялся. Как передать два выхода в diff напрямую?

троичный
источник

Ответы:

246

Подстановка команд подставляет `…`выходные данные команды в командную строку, поэтому diffсписок файлов в обоих каталогах рассматривается как аргументы. Вам нужно diffвидеть два имени файла в его командной строке, и содержимое этих файлов должно быть списком каталогов. Вот что делает процесс подстановки .

diff <(ls old) <(ls new)

Аргументы для diffбудут выглядеть /dev/fd/3и /dev/fd/4: они дескрипторы , соответствующие двух труб , созданных Баш. Когда diffэти файлы откроются, они будут подключены к стороне чтения каждого канала. Сторона записи каждого канала связана с lsкомандой.

жилль
источник
49
echo <(echo) <(echo)никогда не думал, что это может быть так интересно: D
Водолей Power
3
Подстановка процессов поддерживается не всеми оболочками , но перенаправление каналов - это обходной путь .
Irfan434
1
Просто говоря уже о том , что разбор Ls не рекомендуется unix.stackexchange.com/questions/128985/why-not-parse-ls
Кату
@Katu Проблема в lsтом, что он искажает имена файлов. Анализ выходных данных хрупок (он не работает с «странными» именами файлов). Для сравнения двух списков каталогов это нормально, пока вывод однозначен. Для произвольных имен файлов это потребует такой опции, как --quoting-style=escape.
Жиль
1
@will <(…)создает трубу. Кажется, что смесь не работает с трубами, поэтому вы не можете использовать <(…). В zsh вы можете заменить <(…)на, =(…)и это будет работать, потому =(…)что промежуточные выходные данные помещаются во временный файл. В bash я не думаю, что есть какой-то удобный синтаксис, вам придется самостоятельно управлять временными файлами.
Жиль
3

Для zsh использование =(command)автоматически создает временный файл и заменяет =(command)путь к самому файлу. С Подстановкой Команды, $(command)заменяется выходом команды.

Итак, есть три варианта:

  1. Подстановка команд: $(...)
  2. Замена процесса: <(...)
  3. zsh-ароматизированная замена процесса: =(...)

Подстановка процессов с добавлением zsh, # 3, очень полезна и может использоваться для сравнения выходных данных двух команд с использованием инструмента сравнения, например Beyond Compare:

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

Для Beyond Compare обратите внимание, что вы должны использовать bcompвышеперечисленное (вместо bcompare), поскольку bcompзапускает сравнение и ожидает его завершения. Если вы используете bcompare, это запускает сравнение и сразу же завершается, из-за чего временные файлы, созданные для хранения вывода команд, исчезают.

Подробнее читайте здесь: http://zsh.sourceforge.net/Intro/intro_7.html

Также обратите внимание на это:

Обратите внимание, что оболочка создает временный файл и удаляет его после завершения команды.

и следующее, которое является разницей между двумя типами замещения Process, поддерживаемыми zsh (то есть # 2 и # 3):

Если вы прочитаете справочную страницу zsh, вы можете заметить, что <(...) - это другая форма подстановки процесса, аналогичная = (...). Между ними есть важное различие. В случае <(...) оболочка создает именованный канал (FIFO) вместо файла. Это лучше, так как не заполняет файловую систему; но это не работает во всех случаях. Фактически, если бы мы заменили = (...) на <(...) в приведенных выше примерах, все они перестали бы работать, кроме fgrep -f <(...). Вы не можете редактировать канал или открывать его как почтовую папку; Однако у fgrep нет проблем с чтением списка слов из канала. Вы можете задаться вопросом, почему не работает diff <(foo), так как foo | diff - барные работы; это связано с тем, что diff создает временный файл, если он замечает, что одним из его аргументов является -, а затем копирует свой стандартный ввод во временный файл.

Ссылка: https://unix.stackexchange.com/questions/393349/difference-between-subshells-and-process-substitution

Ашутош Джиндал
источник
2
$(...)это не подстановка процессов, это подстановка команд . <(...)это процесс замещения. Вот почему цитируемый отрывок вообще не упоминается $(...).
Муру
2

Рыбная раковина

В оболочке Рыба вы должны трубкой в псуб . Вот пример сравнения конфигурации heroku и dokku с Beyond Compare :

bcompare (ssh me@myapp.pl dokku config myapp | sort | psub) (heroku config -a myapp | sort | psub)
WooYek
источник
1
Другой графический инструмент сравнения с meldоткрытым исходным кодом доступен в репозиториях Ubuntu и EPEL. meldmerge.org
phiphi
0

Я часто использую технику, описанную в принятом ответе:

diff <(ls old) <(ls new)

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

Я считаю, что в 99% случаев я пробую соответствующие команды перед запуском diff. Следовательно, команды, которые я хочу использовать, прямо в моей истории ... почему бы не использовать их?

Я использую встроенную команду Fix Command (fc) bash для выполнения двух последних команд:

$ echo A
A
$ echo B
B
$ diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )
1c1
< B
---
> A

Флаги ФК:

-n : нет номера. Подавляет номера команд при перечислении.

-l : Listing: команды перечислены в стандартном выводе.

-1 -1относятся к начальному и конечному полаганию в истории, в этом случае его от последней команды до последней команды , которая дает только последнюю команду.

Наконец, мы завернем это, $()чтобы выполнить команду в подоболочке.

Очевидно, что набирать текст немного неудобно, поэтому мы можем создать псевдоним:

alias dl='diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )'

Или мы можем создать функцию:

dl() {
    if [[ -z "$1" ]]; then
        first="1"
    else
        first="$1"
    fi
    if [[ -z "$2" ]]; then
        last="2"
    else
        last="$2"
    fi
    # shellcheck disable=SC2091
    diff --color <( $(fc -ln "-$first" "-$first") ) <( $(fc -ln "-$last" "-$last") )
}

который поддерживает указание линий истории для использования. После использования обоих я считаю, что псевдоним - это версия, которую я предпочитаю.

Htaccess
источник