Jak usunąć sufiks pliku i część ścieżki z ciągu ścieżki w Bash?
396
Biorąc pod uwagę ścieżkę do pliku łańcuchowego, na przykład /foo/fizzbuzz.bar, w jaki sposób użyłbym bash do wyodrębnienia tylko fizzbuzzczęści tego łańcucha?
Skończyło się na tym, ponieważ był najbardziej elastyczny i było kilka innych podobnych rzeczy, które chciałem zrobić, a także to, co zrobiłem dobrze.
Lawrence Johnston,
Jest to prawdopodobnie najbardziej elastyczna ze wszystkich opublikowanych odpowiedzi, ale myślę, że odpowiedzi sugerujące polecenia basename i dirname również zasługują na uwagę. Mogą być po prostu sztuczką, jeśli nie potrzebujesz żadnego innego fantazyjnego dopasowania wzoru.
mgadda,
7
Co to nazywa się $ {x% .bar}? Chciałbym dowiedzieć się więcej na ten temat.
Basil
14
@Basil: Rozszerzenie parametru. Na konsoli wpisz „man bash”, a następnie wpisz „/ parameter extension”
Zan Lynx
1
Wydaje mi się, że wyjaśnienie „man bash” ma sens, jeśli już wiesz, co to robi lub jeśli sam to wypróbowałeś. Jest prawie tak zły, jak referencje git. Zamiast tego po prostu google.
Prawdopodobnie najprostsze ze wszystkich obecnie oferowanych rozwiązań ... chociaż użyłbym $ (...) zamiast backticks.
Michael Johnson,
6
Najprostsze, ale dodaje zależność (przyznaję, że nie jest to duża ani dziwna). Musi także znać sufiks.
Vinko Vrsalovic
I może być użyty do usunięcia czegokolwiek z końca, w zasadzie usuwa po prostu ciąg znaków z końca.
Smar,
2
Problemem jest czas uderzenia. Właśnie przeszukałem pytanie w tej dyskusji po tym, jak bash zajął prawie 5 minut na przetworzenie 800 plików przy użyciu basename. Przy użyciu powyższej metody wyrażenia regularnego czas został skrócony do około 7 sekund. Chociaż odpowiedź ta jest łatwiejsza dla programisty, czas na nią jest po prostu zbyt duży. Wyobraź sobie folder zawierający kilka tysięcy plików! Mam kilka takich folderów.
xizdaqrian
@xizdaqrian To absolutnie nieprawda. Jest to prosty program, którego powrót nie powinien zająć pół sekundy. Właśnie wykonałem czas find / home / me / dev -name "* .py" .py -exec basename {} \; i usunął rozszerzenie i katalog dla 1500 plików w sumie 1 sekundę.
Laszlo Treszkai,
40
Pure Bash, wykonane w dwóch osobnych operacjach:
Usuń ścieżkę z ciągu-ścieżki:
path=/foo/bar/bim/baz/file.gif
file=${path##*/} #$file is now 'file.gif'
Używając basename, wykorzystałem następujące do osiągnięcia tego:
for file in*;do
ext=${file##*.}
fname=`basename $file $ext`# Do things with $fnamedone;
Nie wymaga to a priori znajomości rozszerzenia pliku i działa nawet wtedy, gdy nazwa pliku zawiera kropki w nazwie pliku (przed rozszerzeniem); wymaga to basenamejednak programu , ale jest to część jądra GNU, więc powinno być dostarczane z dowolną dystrybucją.
Dobrze byłoby, aby naprawić przytoczyć tutaj - może uruchomić to przez shellcheck.net ze mystring=$1zamiast aktualnej wartości stałej (które tłumią kilka ostrzeżeń, będąc pewny, aby nie zawierać spacji / znaków glob / etc), oraz adres zagadnień IT znajdzie?
Charles Duffy
Cóż, wprowadziłem odpowiednie zmiany w celu obsługi znaków cudzysłowu w $ mystring. Boże, to było dawno temu, napisałem to :)
Jerub
Dalszą poprawą byłoby cytowanie wyników: echo "basename: $(basename "$mystring")"- w ten sposób, jeśli po zakończeniu mystring='/foo/*'nie zostanie *zastąpiona lista plików w bieżącym katalogu . basename
Charles Duffy
6
Używanie basenamezakłada, że wiesz, jakie jest rozszerzenie pliku, prawda?
I wierzę, że różne propozycje wyrażeń regularnych nie radzą sobie z nazwą pliku zawierającą więcej niż jeden „.”
Poniższe wydaje się radzić sobie z podwójnymi kropkami. Aha, i nazwy plików, które same zawierają „/” (tylko dla kopnięć)
Parafrazując Pascala, „Przepraszam, że ten skrypt jest tak długi. Nie miałem czasu, aby go skrócić”
Można to nawet zabezpieczyć pod nazwą pliku, oddzielając dane wyjściowe bajtami NUL za pomocą -zopcji, na przykład dla plików zawierających spacje, znaki nowej linii i znaki globalne (cytowane przez ls):
$ ls has*'has'$'\n''newline.bar''has space.bar''has*.bar'
Jeśli nie możesz użyć basename jak sugerowano w innych postach, zawsze możesz użyć sed. Oto (brzydki) przykład. Nie jest najlepszy, ale działa poprzez wyodrębnienie poszukiwanego ciągu i zastąpienie wejścia żądanym ciągiem.
echo '/foo/fizzbuzz.bar'| sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Chociaż jest to odpowiedź na pierwotne pytanie, to polecenie jest przydatne, gdy w pliku mam linie ścieżek do wyodrębnienia podstawowych nazw i wydrukowania ich na ekranie.
Sangcheol Choi,
2
Uwaga na sugerowane rozwiązanie perla: usuwa wszystko po pierwszej kropce.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Jeśli chcesz to zrobić za pomocą Perla, działa to:
Basename to robi, usuwa ścieżkę. Usunie również sufiks, jeśli jest podany i jeśli pasuje do sufiksu pliku, ale trzeba znać przyrostek, aby podać polecenie. W przeciwnym razie możesz użyć mv i dowiedzieć się, jaka powinna być nowa nazwa.
${parameter%word}
i${parameter%%word}
w części dotyczącej dopasowywania części końcowej.Odpowiedzi:
Oto jak to zrobić za pomocą operatorów # i% w Bash.
${x%.bar}
może być również${x%.*}
usunięcie wszystkiego po kropce lub${x%%.*}
usunięcie wszystkiego po pierwszej kropce.Przykład:
Dokumentację można znaleźć w instrukcji Bash . Szukaj
${parameter%word}
i${parameter%%word}
dopasowuj sekcję dopasowywania porcji.źródło
spójrz na polecenie basename:
źródło
Pure Bash, wykonane w dwóch osobnych operacjach:
Usuń ścieżkę z ciągu-ścieżki:
Usuń rozszerzenie z ciągu ścieżki:
źródło
Używając basename, wykorzystałem następujące do osiągnięcia tego:
Nie wymaga to a priori znajomości rozszerzenia pliku i działa nawet wtedy, gdy nazwa pliku zawiera kropki w nazwie pliku (przed rozszerzeniem); wymaga to
basename
jednak programu , ale jest to część jądra GNU, więc powinno być dostarczane z dowolną dystrybucją.źródło
fname=`basename $file .$ext`
Pure Bash Way:
Ta funkcjonalność została wyjaśniona w podręczniku man bash w „Rozszerzeniu parametrów”. Istnieje wiele sposobów na bash: awk, perl, sed i tak dalej.
EDYCJA: Działa z kropkami w sufiksach plików i nie musi znać sufiksu (rozszerzenia), ale nie działa z kropkami w samej nazwie .
źródło
Funkcje basename i dirname są tym, czego szukasz:
Ma moc wyjściową:
źródło
mystring=$1
zamiast aktualnej wartości stałej (które tłumią kilka ostrzeżeń, będąc pewny, aby nie zawierać spacji / znaków glob / etc), oraz adres zagadnień IT znajdzie?echo "basename: $(basename "$mystring")"
- w ten sposób, jeśli po zakończeniumystring='/foo/*'
nie zostanie*
zastąpiona lista plików w bieżącym katalogu .basename
Używanie
basename
zakłada, że wiesz, jakie jest rozszerzenie pliku, prawda?I wierzę, że różne propozycje wyrażeń regularnych nie radzą sobie z nazwą pliku zawierającą więcej niż jeden „.”
Poniższe wydaje się radzić sobie z podwójnymi kropkami. Aha, i nazwy plików, które same zawierają „/” (tylko dla kopnięć)
Parafrazując Pascala, „Przepraszam, że ten skrypt jest tak długi. Nie miałem czasu, aby go skrócić”
źródło
Oprócz składni zgodnej z POSIX zastosowanej w tej odpowiedzi ,
jak w
GNU
basename
obsługuje inną składnię:z tym samym wynikiem. Różnica i zaletą jest to
-s
implikuje-a
, który obsługuje wiele argumentów:Można to nawet zabezpieczyć pod nazwą pliku, oddzielając dane wyjściowe bajtami NUL za pomocą
-z
opcji, na przykład dla plików zawierających spacje, znaki nowej linii i znaki globalne (cytowane przezls
):Odczytywanie do tablicy:
readarray -d
wymaga wersji Bash 4.4 lub nowszej. W przypadku starszych wersji musimy zapętlić:źródło
źródło
Jeśli nie możesz użyć basename jak sugerowano w innych postach, zawsze możesz użyć sed. Oto (brzydki) przykład. Nie jest najlepszy, ale działa poprzez wyodrębnienie poszukiwanego ciągu i zastąpienie wejścia żądanym ciągiem.
Co da ci wynik
źródło
Uwaga na sugerowane rozwiązanie perla: usuwa wszystko po pierwszej kropce.
Jeśli chcesz to zrobić za pomocą Perla, działa to:
Ale jeśli używasz Basha, rozwiązania z
y=${x%.*}
(lubbasename "$x" .ext
jeśli znasz rozszerzenie) są znacznie prostsze.źródło
Basename to robi, usuwa ścieżkę. Usunie również sufiks, jeśli jest podany i jeśli pasuje do sufiksu pliku, ale trzeba znać przyrostek, aby podać polecenie. W przeciwnym razie możesz użyć mv i dowiedzieć się, jaka powinna być nowa nazwa.
źródło
Łącząc najwyżej ocenianą odpowiedź z drugą najwyżej ocenianą odpowiedzią, aby uzyskać nazwę pliku bez pełnej ścieżki:
źródło