Jak przekazać symbol wieloznaczny „*” do parametru ścieżki polecenia find za pomocą zmiennej w skrypcie?


9

Chcę używać finddo znajdowania plików w zestawie folderów ograniczonych symbolami wieloznacznymi, ale tam, gdzie w nazwie ścieżki są spacje.

Z linii poleceń jest to łatwe. Wszystkie poniższe przykłady działają.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Znajdą one na przykład pliki terminal/my files/morei tepid/my files/more.

Potrzebuję tego jednak, aby był częścią skryptu; potrzebuję czegoś takiego:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

Niestety, cokolwiek robię, nie jestem w stanie mieszać symboli wieloznacznych i spacji w findpoleceniu w skrypcie. Powyższy przykład zwraca następujące błędy (zwróć uwagę na nieoczekiwane podwojenie odwrotnego ukośnika):

find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory

Próba użycia cytatów również się nie udaje.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Zwraca następujące błędy, ignorując znaczenie cudzysłowów:

find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Oto jeszcze jeden przykład.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Zgodnie z oczekiwaniami:

find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory

Każda odmiana, którą próbowałem, zwraca błąd.

Mam obejście, które jest potencjalnie niebezpieczne, ponieważ zwraca zbyt wiele folderów. Konwertuję wszystkie spacje na znak zapytania (jednoznakowy symbol wieloznaczny) w następujący sposób:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

Jest to odpowiednik:

find te*/my?files/more -print

Zwraca to nie tylko prawidłowe foldery, ale także terse/myxfiles/more, czego nie powinno.

Jak mogę osiągnąć to, co próbuję zrobić? Google mi nie pomogło :(


@KasiyA Używam bash; musisz używać czegoś innego, ponieważ wcześniej nie widziałem tej konstrukcji. Polecenie powoduje SEARCH: command not foundwykonanie polecenia find -print.
— Paddy Landau

Strzał w ciemności, ale co z cytowaniem? find "${SEARCH}" -print?
— Alaa Ali,

@AlaaAli Nie, to nie działa, ponieważ cytowanie uniemożliwia Bashowi używanie symboli wieloznacznych. Będzie szukał ścieżki konkretnie z nazwą (w moim przykładzie) te*/'my files'/more.
— Paddy Landau

Odpowiedzi:


9

Dokładnie to samo polecenie powinno działać poprawnie w skrypcie:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Jeśli potrzebujesz mieć ją jako zmienną, staje się ona nieco bardziej złożona:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

OSTRZEŻENIE:

Używanie w evalten sposób nie jest bezpieczne i może spowodować wykonanie dowolnego i potencjalnie szkodliwego kodu, jeśli nazwy plików mogą zawierać określone znaki. Szczegółowe informacje można znaleźć w bash FAQ 48 .

Lepiej podać ścieżkę jako argument:

#!/usr/bin/env bash
find "$@" -name "file*"

Innym podejściem jest findcałkowite unikanie i korzystanie z rozszerzonych funkcji globowania i globów bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

Opcja globstarbash umożliwia **dopasowanie rekurencyjne:

globstar
      If set, the pattern ** used in a pathname expansion con‐
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Aby działał w 100% jak znajdź i dołącz pliki dot (pliki ukryte), użyj

#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done

Możesz nawet echoje bezpośrednio bez pętli:

echo te*/my\ files/**

2
Ustawiłem to jako odpowiedź ze względu na pomocne komentarze, które wypowiedział terdon (nie zapominając o pomocnych komentarzach innych). Użyłem możliwości globowania Basha w wierszu poleceń, aby przekazać wiele ścieżek do mojego skryptu, zamiast skryptu próbującego go rozwiązać. To dobrze działa.
— Paddy Landau

2

Co powiesz na tablice?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*)rozwija się do tablicy wszystkiego, co pasuje do znaku wieloznacznego. I "${SEARCH[@]}"rozwija się do wszystkich elementów w tablicy ( [@]), każdy z osobna cytowany.

Z opóźnieniem zdaję sobie sprawę, że powinna być w stanie to zrobić. Coś jak:

find . -path 'D*/my folder/more/'

Sprytny pomysł, ale niestety to nie działa. Dlaczego? Ponieważ sama ścieżka jest utrzymywana w zmiennej; stąd INPUTPATH='te*/my files/morei SEARCH=(${INPUTPATH}). Bez względu na to, jak zmieniam sposób, w jaki to robię, nadal mam wynik niefunkcjonalny. To wydaje się niemożliwe!
— Paddy Landau

To wszystko oczywiście prawda, ale OP musi to zrobić w skrypcie. To zmienia rzeczy, ponieważ ekspansja symboli wieloznacznych staje się znacznie bardziej złożona i to nie działa.
— terdon

@PaddyLandau W takim razie, dlaczego nie można użyć find„s -pathfiltr? Używa symboli wieloznacznych i zdecydowanie nie wymaga rozszerzenia.
— mur

@muru To ciekawe; Nie wiedziałam o tym -path. Jednak w ciągu ostatnich 10 minut wymyśliłem odpowiedź: użyj eval! Wydaje się to prostsze niż -path.
— Paddy Landau

2
@terdon i muru i wszyscy: dziękuję. Słyszałem, co wszyscy mówiliście, i zdałem sobie sprawę, że powinienem zmusić mój skrypt do zrobienia jednej rzeczy i pozwolić Bashowi na globbing przekazywać wiele plików lub ścieżek do skryptu. W ten sposób zmodyfikowałem skrypt. Działa dobrze i lepiej pasuje do filozofii Linux. Jeszcze raz dziękuję!
— Paddy Landau

0

W końcu znalazłem odpowiedź.

Dodaj ukośnik do wszystkich spacji:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }

W tym momencie SEARCHzawiera te*/my\ files/more.

Następnie użyj eval.

eval find ${SEARCH} -print

To takie proste! Użycie evalpomija interpretację ${SEARCH}pochodzącą ze zmiennej.



@terdon Dziękuję za ostrzeżenie. Wróć do deski kreślarskiej!
— Paddy Landau

Tak, to zaskakująco trudne. Właśnie zaktualizowałem swoją odpowiedź innym podejściem, dlaczego zamiast tego nie użyć globowania? Jeśli to nadal nie działa, sugeruję, abyś opublikował nowe pytanie na Unixie i Linuksie wyjaśniające, jaki jest twój ostateczny cel i dlaczego musisz mieć wzorzec jako zmienną. Tego rodzaju rzeczy są bardziej prawdopodobne, aby uzyskać lepszą odpowiedź.
— terdon
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.