W jaki sposób „find -exec” przekazuje nazwy plików ze spacjami?

14

Jeśli mam katalog zawierający niektóre pliki, których nazwy zawierają spacje, np

$ ls -1 dir1
file 1
file 2
file 3

Mogę z powodzeniem skopiować wszystkie z nich do innego katalogu, takiego jak ten:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Jednak dane wyjściowe find dir1 -mindepth 1zawierają spacje bez znaku ucieczki:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Jeśli użyję print0zamiast print, dane wyjściowe nadal zawierają spacje bez znaczenia:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Aby ręcznie skopiować te pliki za pomocą cp, musiałbym uciec od spacji; ale wydaje się, że nie jest to konieczne, gdy cppochodzą narzędzia find, bez względu na to, czy używam polecenia, +czy \;na jego końcu.

Jaki jest tego powód?

EmmaV
źródło

Odpowiedzi:

8

findPolecenie wykonuje polecenie bezpośrednio. Polecenie, w tym argument nazwy pliku, nie będzie przetwarzane przez powłokę ani nic innego, co mogłoby zmodyfikować nazwę pliku. To jest bardzo bezpieczne.

Masz rację, że nie ma potrzeby zmiany nazw plików reprezentowanych przez {}w findwierszu poleceń.

findprzekazuje surową nazwę pliku z dysku bezpośrednio na wewnętrzną listę argumentów -execpolecenia, w twoim przypadku cppolecenia.

RobertL
źródło
1
Krótko mówiąc, find..execpotrafi samodzielnie radzić sobie z dziwnymi nazwami plików.
heemayl
2
Pierwszą zasadą klubu linux jest to, że nie analizujesz ls
Sergiy Kolodyazhnyy
5

Pytanie składa się z dwóch części:

  • jak radzi find sobie z wywoływaniem programów -execbez problemów ze spacjami osadzonymi w nazwach plików, oraz
  • jaka jest dobra -print0opcja?

Po pierwsze, findjest to wywołanie systemowe, a właściwie jedna z grupy powiązanych wywołań zwanych „exec” . Przekazuje nazwę pliku jako argument bezpośrednio do tego wywołania, które następnie jest przekazywane bezpośrednio (po utworzeniu nowego procesu) bez utraty informacji o nazwie pliku.

POSIX findcechą +jest wyjaśnione następująco w przesłankach :

Cechą findnarzędzia SVR4 był -execterminator + elementu pierwotnego. Umożliwiło to grupowanie nazw plików zawierających znaki specjalne (zwłaszcza znaki nowego wiersza ) bez problemów, które występują, jeśli takie nazwy plików są przesyłane potokowo xargs. Inne implementacje dodały inne sposoby obejścia tego problemu, w szczególności -print0podstawowy, który zapisywał nazwy plików z terminatorem zerowym. Zostało to rozważone tutaj, ale nie przyjęte. Użycie terminatora zerowego oznaczało, że każde narzędzie, które miało przetwarzać -print0dane wyjściowe find, musiało dodać nową opcję do parsowania terminatorów zerowych, które teraz będzie czytać.

Ten „ w szczególności-print0 podstawowy” odnosi się do GNU findi xargsktóry rozwiązuje ten problem w inny sposób. Jest także obsługiwany przez FreeBSD findi xargs. Jeśli dodałeś -0opcję (patrz strona instrukcji ) do xargswywołania, wówczas ten program akceptuje linie zakończone znakami „null byte”. Z kolei xargswywołuje exec -functions, aby wykonać swoją pracę. Główne rozróżnienie między -print0i -0a +cechą polega na tym, że ten pierwszy przekazuje nazwy plików przez potok, a drugi nie. Programiści znajdują zastosowania dla prawie każdej funkcji; rury nie są wyjątkiem.

Powrót do przykładu OP, w którym zastosowano -topcję cp: nie znaleziono w POSIX cp . Jest to raczej rozszerzenie (inaczej „niestandardowa funkcja”) dostarczone przez GNU cp . -0Rozszerzenie xargsnie poprawi ten przykład, ale istnieją inne przypadki, w których może być skutecznie wykorzystywane-pamiętając, że istnieje alternatywa przenośny +, który GNU findakceptuje.

Thomas Dickey
źródło
-1

( To powinien być komentarz, ale jest za duży. )

Dla tych, którzy lubią wypróbować:

Utwórz skrypt zawierający parametry pozycyjne przekazane, wywołaj go list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Uruchom findgo w katalogu $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Zgodnie z oczekiwaniami we wszystkich wywołaniach jest tylko jeden parametr, nazwa pliku, niezależnie od tego, czy w nazwie znajdują się spacje.

David Tonhofer
źródło
1
Można również używać printfjak printf '"%s"\n' "$@"wydrukować wszystkie pozycyjnym argumentu cytowany dla inspekcji wzrokowej.
Kusalananda