Использование данных, прочитанных из канала, а не из файла в параметрах команды

18

По определению человека эта команда получает входные данные из файла.

$ command -r FILENAME

Предположим, что FILENAMEэто файл, содержащий список имен файлов, как это было сгенерировано с помощью ls > FILENAME.

Как я могу вместо этого кормить команду результатом lsнепосредственно? В моей голове что-то вроде этого должно быть возможно:

$ ls | command -r

Но это не так, вывод lsне попадает в качестве аргумента. Выход:

Usage: command -r FILENAME
error: -r option requires an argument

Как я мог получить желаемый эффект?

Paolo
источник
Больше ответов на этот вопрос в связанном вопросе: Как передать строку команде, которая ожидает файл?
ilkkachu

Ответы:

31

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

  • Многие команды обрабатываются -как специальное имя, означающее чтение из стандартного ввода, а не открытие файла. Это соглашение, а не обязательство.

    ls | command -r -
  • Многие варианты Unix предоставляют специальные файлы, /devкоторые обозначают стандартные дескрипторы. Если /dev/stdinсуществует, его открытие и чтение из него эквивалентно чтению из стандартного ввода; Точно так же, /dev/fd/0если он существует.

    ls | command -r /dev/stdin
    ls | command -r /dev/fd/0
  • Если вашей оболочкой является ksh, bash или zsh, вы можете заставить оболочку заниматься выделением некоторого файлового дескриптора. Основным преимуществом этого метода является то, что он не привязан к стандартному вводу, поэтому вы можете использовать стандартный ввод для чего-то другого, и вы можете использовать его более одного раза.

    command -r <(ls)
  • Если команда ожидает, что имя будет иметь конкретную форму (как правило, конкретное расширение), вы можете попытаться обмануть его с помощью символической ссылки.

    ln -s /dev/fd/0 list.foo
    ls | command -r list.foo

    Или вы можете использовать именованный канал.

    mkfifo list.foo
    ls >list.foo &
    command -r list.foo

Обратите внимание, что создание списка файлов с lsпроблемой является проблематичным, поскольку lsимеет тенденцию искажать имена файлов, когда они содержат непечатаемые символы. printf '%s\n' *более надежен - он печатает каждый байт буквально в именах файлов. Имена файлов, содержащие символы новой строки, все равно будут вызывать проблемы, но это неизбежно, если команда ожидает список имен файлов, разделенных символами новой строки.

Жиль "ТАК - прекрати быть злым"
источник
+1 за перечисление всех возможностей. Подстановка процесса IMO - правильный ответ для этой ситуации.
Гленн Джекман
7

Так должно быть:

ls | xargs -n 1 command -r

Изменить: для имен с пробелами:

ls | xargs -d '\n' -n 1 command -r
ghm1014
источник
Это может быть проблематично, если commandон считает, что в командной строке указаны все имена файлов, которые ему понадобятся. Некоторые версии xargs дают commandнесколько имен файлов (мне кажется, 10) за раз. Похоже, GNU xargs дает все сразу.
Брюс Эдигер
2

На самом деле, единственное надежное решение, которое мне известно, которое может обрабатывать все имена файлов, в том числе с новой строкой, это:

find . -maxdepth 1 -print0 | xargs -n 1 -0 command -r

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

Линус Свелас
источник
1

Многие команды принимают -как «имя файла», что означает «использовать стандартный ввод», но это соглашение далеко не универсально. Прочитайте справочную страницу.

dmckee
источник
1

Это должно работать для вашей цели: ls | command -r /dev/stdin

Алекс
источник