find-exec a shell function in Linux?

Czy istnieje sposób, aby find wykonać funkcję, którą zdefiniuję w powłoce? Na przykład:

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

Wynikiem tego jest:

find: dosomething: No such file or directory

Czy Jest jakiś sposób, aby find ' S -exec zobaczyć dosomething?

Author: jww, 2010-12-01

12 answers

Ponieważ tylko powłoka wie, jak uruchamiać funkcje powłoki, musisz uruchomić powłokę, aby uruchomić funkcję. Musisz również oznaczyć funkcję do eksportu export -f, w przeciwnym razie subshell nie odziedziczy ich:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
 197
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-22 05:13:58
find . | while read file; do dosomething "$file"; done
 83
Author: Jac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 12:39:32

Dodaj cytaty w {[1] } Jak pokazano poniżej:

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

To koryguje wszelkie błędy spowodowane znakami specjalnymi zwracanymi przez find, na przykład pliki z nawiasami w nazwie.

 17
Author: Wagner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-16 02:29:20

Odpowiedź jak powyżej jest świetna, ale ma kilka pułapek, które można łatwo pokonać:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

To używa null jako ogranicznika zamiast linefeed, więc nazwy plików z linefeeds będą działać. Używa również znacznika -r, który wyłącza unikanie ukośników wstecznych, bez niego ukośniki w nazwach plików nie będą działać. Czyści również IFS, aby potencjalne końcowe spacje w nazwach nie były odrzucane.

 12
Author: pajamian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-13 21:55:22

Wykonaj samo wywołanie skryptu, przekazując każdy znaleziony element jako argument:

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

Gdy uruchomisz skrypt samodzielnie, znajdzie to, czego szukasz i wywoła się z każdego wyniku wyszukiwania jako argumentu. Gdy skrypt jest uruchamiany z argumentem, wykonuje polecenia na argumencie, a następnie kończy działanie.

 8
Author: Mike Maready,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-22 18:10:38

Wyniki przetwarzania luzem

W celu zwiększenia wydajności, Wiele osób używa xargs do przetwarzania wyników luzem, ale jest to bardzo niebezpieczne. Z tego powodu do find została wprowadzona alternatywna metoda, która wykonuje wyniki masowo.

Zauważ jednak, że ta metoda może zawierać pewne zastrzeżenia, takie jak na przykład wymóg w POSIX-find, aby mieć {} na końcu polecenia.

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

find przekazuje wiele wyników jako argumenty do jednego wywołania bash i for - pętla iteruje przez te argumenty, wykonując funkcję dosomething na każdym z nich.

Powyższe rozwiązanie rozpoczyna argumenty od $1, dlatego istnieje _ (która reprezentuje $0).

Wyniki przetwarzania jeden po drugim

W ten sam sposób, myślę, że przyjęta odpowiedź powinna być poprawiona

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

Jest to nie tylko bardziej rozsądne, ponieważ argumenty powinny zawsze zaczynać się od $1, ale także użycie $0 może prowadzić do nieoczekiwane zachowanie, jeśli nazwa pliku zwracana przez find ma specjalne znaczenie dla powłoki.

 6
Author: Dominik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 18:33:00

Dla tych z Was, którzy szukają funkcji bash, która wykona daną komendę na wszystkich plikach w bieżącym katalogu, skompilowałem jedną z powyższych odpowiedzi:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

Zauważ, że łamie się z nazwami plików zawierającymi spacje (patrz poniżej).

Jako przykład, weźmy tę funkcję:

world(){
    sed -i 's_hello_world_g' "$1"
}

Powiedzmy, że chciałem zmienić wszystkie instancje hello to world we wszystkich plikach w bieżącym katalogu. Zrobiłbym:

toall world

Aby być bezpiecznym z dowolnymi symbolami w nazwach plików, użyj:

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(ale potrzebujesz find, który obsługuje -print0 np. GNU find).

 2
Author: Jason Basanese,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-09 17:54:51

Nie jest możliwe wykonanie funkcji w ten sposób.

Aby to przezwyciężyć, możesz umieścić swoją funkcję w skrypcie powłoki i wywołać ją z find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

Teraz użyj go w find jako:

find . -exec dosomething.sh {} \;
 1
Author: codaddict,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-01 05:29:00

Umieść funkcję w osobnym pliku i get find, aby ją wykonać.

Funkcje powłoki są wewnętrzne do powłoki, w której są zdefiniowane; find nigdy nie będą w stanie ich zobaczyć.

 1
Author: Angus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-01 05:29:14

Znajduję najprostszy sposób jak poniżej, powtarzając dwa polecenia w jednym do

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done
 1
Author: edib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-07 09:12:02

Nie bezpośrednio, nie. Find jest wykonywany w osobnym procesie, a nie w powłoce.

Utwórz skrypt powłoki, który wykonuje to samo zadanie co twoja funkcja i znajdź -exec to.

 0
Author: Laurence Gonsalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-01 05:27:57

W ogóle nie używałbym -exec. Dlaczego nie użyć xargs?

find . -name <script/command you're searching for> | xargs bash -c
 -4
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-11 22:16:21