Xargs с несколькими командами

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

find . | grep "file_for_print" | xargs echo
find . | grep "file_for_print" | xargs cat

но я хочу, чтобы они печатали их следующим образом:

file1
line1 inside file1
line2 inside file1
file2
line1 inside file2
line2 inside file2

Я читаю xargs с несколькими командами в качестве аргумента и попробовал

find . | grep "file_for_print" | xargs -I % sh -c 'echo; cat;'

но не работает. Я не знаком с xargs, поэтому не знаю, что именно означает "-I% sh -c". Может ли кто-нибудь мне помочь? спасибо!

Ответ 1

Для начала нет никакой разницы между:

find . | grep "file_for_print" | xargs echo

и

find . -name "file_for_print*"

за исключением того, что второй не будет соответствовать именам файлов, например this_is_not_the_file_for_print, и он будет печатать имена файлов по одному в каждой строке. Это также будет намного быстрее, потому что ему не нужно создавать и печатать всю рекурсивную структуру каталогов только для того, чтобы grep удалял большую часть.

find . -name "file_for_print*"

на самом деле точно совпадает с

find . -name "file_for_print*" -print

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

find . -name "file_for_print*" -exec cat {} \;

Действие -exec приводит к тому, что find выполняет следующую команду, вплоть до \;, заменяя {} каждым соответствующим именем файла.

find не ограничивается одним действием. Вы можете сказать, что делаете, сколько хотите. Итак:

find . -name "file_for_print*" -print -exec cat {} \;

вероятно, сделает все, что вам нужно.

Для получения дополнительной информации об этой очень полезной утилите введите:

man find

или

info find

и прочитайте все о нем.

Ответ 2

find . | grep "file_for_print" | xargs -I % sh -c 'echo %; cat %;' (OP отсутствовал % s)

Ответ 3

Так как еще не сказано: -I % указывает xargs заменить "%" на аргументы в команде, которую вы ему даете. sh -c '...' просто означает выполнение команд '...' в новой оболочке.

So

xargs -I % sh -c 'echo %; cat %;'

будет выполняться echo [filename], за которым следует cat [filename] для каждого имени файла, указанного в xargs. Команды echo и cat будут выполняться внутри другого процесса оболочки, но это обычно не имеет значения. Ваша версия не работала, потому что в команде, переданной в xargs, отсутствовали знаки %.


Для чего я должен использовать эту команду для достижения того же:

find -name "*file_for_print*" | parallel 'echo {}; cat {};'

потому что он проще (parallel автоматически использует {} в качестве символа подстановки и может принимать несколько команд по умолчанию).

Ответ 4

Как написать собственную функцию bash?

#!/bin/bash

myFunction() {
    while read -r file; do
        echo "$file"
        cat "$file"
    done
}

find . -name "file_for_print*" | myFunction

Ответ 5

В этом конкретном случае каждая команда выполняется для каждого отдельного файла в любом случае, поэтому нет преимущества при использовании xargs. Вы можете просто добавить -exec дважды в свой "find":

find . -name "*file_for_print*" -exec echo {} \; -exec cat {} \;

В этом случае -print можно использовать вместо первого echo, как указано rici, но этот пример показывает возможность выполнения двух произвольных команд с помощью одного find