find -exec з декількома командами


429

Я намагаюся без особливих успіхів використовувати find -exec з кількома командами. Хтось знає, чи можливі такі команди, як наведено нижче?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

В основному, я намагаюся надрукувати останній рядок кожного файлу txt у поточному каталозі та надрукувати в кінці рядка, кома з наступним іменем файлу.



1
Що стосується перевірки можливості команди, ви не спробували її у своїй системі?
Шрірам

1
Зі findсторінки керівництва: There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999


@ JVE999 посилання розірвано, альтернатива на ss64.com/bash/find.html
Keith M

Відповіді:


658

findприймає кілька -execкоманд до команди. Наприклад:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

Зауважте, що в цьому випадку друга команда буде виконуватися лише в тому випадку, якщо перша успішно повернеться, як зазначає @Caleb. Якщо ви хочете, щоб обидві команди виконувались незалежно від їх успіху чи відмови, ви можете використовувати цю конструкцію:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

як грепнути двічі? це не вдається: знайти ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "my_string" {} \;
rajeev

51
@rajeev Другий виконавець запуститься лише в тому випадку, якщо код повернення для першого поверне успіх, інакше він буде пропущений. Це, мабуть, слід зазначити у цій відповіді.
Калеб

1
Це була б відповідь
піловер

1
Зверніть увагу на використання -nдеяких інших відповідей для придушення нового рядка, породженого echo, що зручно, якщо ваша друга команда створює лише один рядок виводу, і ви хочете, щоб їх було легше читати.
Вільям Террелл

Прекрасне рішення. Працює як шарм.
Філіп Дельтейль

98
find . -type d -exec sh -c "echo -n {}; echo -n ' x '; echo {}" \;

3
Якщо ви хочете запустити Bash замість Bourne, ви можете також використовувати ... -exec bash -c ...замість ... -exec sh -c ....
Кенні Евітт

9
Ніколи не вбудовуйте {}код оболонки. Дивіться unix.stackexchange.com/questions/156008/…
Kusalananda

3
+1 @Kusalananda Ін'єкційні назви файлів є крихкими та небезпечними. Використовуйте параметри. дивіться SC2156
памбда

52

Одне з наступних:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
Для чого підкреслення раніше {}?
qed

2
@qed: Це викидаюче значення, яке займає місце $0. Спробуйте це з "foobar" замість "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- вихід: "[foobar] [/ usr / bin / find]".
Призупинено до подальшого повідомлення.

1
@XuWang: Так, я б сказав, що так. Як відомо, $0зазвичай це назва програми ( ARGV[0]).
Призупинено до подальшого повідомлення.

3
Для цього методу критично важливо, щоб переданий сценарій містився sh -cв одиничних лапках, а не у подвійних. В іншому випадку $1це неправильно.
Нік

1
Котирування @Nick не мають до цього нічого спільного - ви можете писати '$1'подвійними лапками, поки уникнете змінної ( "\$1"). Ви також можете уникати інших символів ( "\"").
Каміло Мартін

22

Ще один спосіб такий:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

в один рядок

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • " multiple_cmd()" - це функція
  • " export -f multiple_cmd" - експортуватиме його, щоб побачити його будь-яка інша підкабель
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - знайдіть, що виконає функцію на вашому прикладі

Таким чином multiple_cmd може бути таким же довгим і таким же складним, як вам потрібно.

Сподіваюсь, це допомагає.


ідеально, просто те, що мені було потрібно!
Анентроп

не працює для мене на OSX
Томас

@Thomas це, але спробуйте цей вкладиш, протестований в osx. Я створив каталог під назвою "aaa" з деякими файлами / dirs там і CDD до нього. Потім, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
барлоп

@barlop, я спробую це спробувати; Дякую!
Томас

14

Є простіший спосіб:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

Як варіант:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
Імена файлів можуть містити в них нові рядки, тому аргумент find має аргумент
-print0,

@abasterfield Я завжди сподіваюся, що ніколи не знайду тих, хто знаходиться в дикій природі, LOL
Camilo Martin

1
що я хотів зробити, це "знайти ... -exec zcat {} | wc -l \;" яка не працювала. Однак знайдіть ... | під час читання -r файл; do echo "$ file: zcat $file | wc -l"; зроблено робить роботу, тож дякую!
Грег Даггерті

У коментарі вище у мене є "зворотні тики" навколо "zcat $ file | wc -l". На жаль, SO перетворює їх у форматування, тому я додав його як фактичну відповідь із правильним кодом, що видно
Грег Даггерті

1
@GregDougherty Ви можете уникнути лапки , `щоб зробити , що ви використовуєте зворотні косі подобаються так: \​`( до сих пір, що це ще одна причина , щоб використовувати $()замість цього).
Каміло Мартін

8

Я не знаю, чи можна це зробити з find, але альтернативним рішенням буде створити скрипт оболонки і запустити це з find.

lastline.sh:

echo $(tail -1 $1),$1

Зробіть сценарій виконуваним

chmod +x lastline.sh

Використання find:

find . -name "*.txt" -exec ./lastline.sh {} \;

7
зворотні посилання застарілі, будь ласка, заохочуйте використовувати $ (...), що краще читається, залежно від шрифту та легко гніздо. Дякую.
користувач невідомий

4

Перша відповідь Дениса - це відповідь на вирішення проблеми. Але насправді це вже не знахідка з кількома командами лише в одному виконанні, як підказує заголовок. Щоб відповісти одному виконавцю з кількома командами, нам доведеться шукати щось інше, щоб вирішити. Ось приклад:

Зберігайте останні 10000 рядків файлів .log, які були змінені за останні 7 днів, використовуючи 1 команду exec, використовуючи декілька посилань {}

1) подивіться, що команда буде робити з якими файлами:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2) Зробіть це: (зверніть увагу не більше "\>", а лише ">" це потрібно)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


Але це зламається, якщо в одній з імен файлів є пробіл, я вважаю.
Каміло Мартін

4

Завдяки Каміло Мартіну я зміг відповісти на відповідне запитання:

Що я хотів зробити

find ... -exec zcat {} | wc -l \;

яка не працювала. Однак,

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

працює, тож дякую!


2

Розширення відповіді @ Тінкера,

У моєму випадку мені потрібно було зробити command | command | commandвсередині, -execщоб надрукувати як ім’я файлу, так і знайдений текст у файлах, що містять певний текст.

Я зміг це зробити за допомогою:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

результат:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
Ви також можете надрукувати ім'я файлу та вміст grep'd в одному рядку, передавши /dev/nullв якості другого аргументу grepкоманду з одним -execпараметром:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

слід використовувати xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

ще один (працює над osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
Це оглядає головний випадок використання find- ситуації, коли кількість відповідних файлів занадто велика для командного рядка.-execце спосіб подолати цю межу. Передача комунальних послуг втрачає цю вигоду.
Кріс Джонсон

xargs -nіснує, щоб вибрати кількість збігів на виклик. xargs -n 1 foocmdбуде виконуватися foocmd {}за кожен матч.
AndrewF

0

find+xargsВідповідь.

Наведений нижче приклад знаходить усі .htmlфайли та створює копію із .BAKдоданим розширенням (наприклад, 1.html> 1.html.BAK).

Одна команда з кількома заповнювачами

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

Кілька команд з кількома заповнювачами

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

Ця команда також буде працювати з файлами, які починаються з дефісу або містять пробіли, такі як -my file.htmlзавдяки цитуванню параметрів і --після cpяких надсилає сигнал до cpкінця параметрів та початку фактичних імен файлів.

-print0 передає результати за допомогою нульових байтових термінаторів.


для xargs-I {} параметр визначає {}як заповнювач; ви можете використовувати який завгодно заповнювач; -0вказує на те, що елементи введення розділені нулем.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.