Chcę osobno pobrać nazwę pliku (bez rozszerzenia) i rozszerzenie.
Najlepszym rozwiązaniem, jakie do tej pory znalazłem, jest:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Jest to złe, ponieważ nie działa, jeśli nazwa pliku zawiera wiele .
znaków. Jeśli, powiedzmy, mam a.b.js
, rozważy a
i b.js
zamiast a.b
i js
.
Można to łatwo zrobić w Pythonie za pomocą
file, ext = os.path.splitext(path)
ale wolałbym nie odpalać interpretera Pythona tylko w tym celu, jeśli to możliwe.
Jakieś lepsze pomysły?
extension="{$filename##*.}"
tak jak robiłem przez pewien czas! Przenieś$
kręcone poza: w prawo:extension="${filename##*.}"
os.path.splitext
zamiast tego użyć Pythona jak wyżej ...Odpowiedzi:
Najpierw uzyskaj nazwę pliku bez ścieżki:
Alternatywnie możesz skupić się na ostatnim „/” ścieżki zamiast „.” który powinien działać, nawet jeśli masz nieprzewidywalne rozszerzenia plików:
Możesz sprawdzić dokumentację:
źródło
basename
extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')
. Pamiętaj, że jeśli rozszerzenie jest obecne, zostanie ono zwrócone wraz z początkowym.
, np.txt
.Aby uzyskać więcej informacji, zobacz rozszerzenie parametrów powłoki w podręczniku Bash.
źródło
dinosaurs.in.tar
idinosaurs.in.tar.gz
x.tar.gz
ma rozszerzenia,gz
a nazwa pliku jestx.tar
taka. Nie ma czegoś takiego jak podwójne rozszerzenia. jestem całkiem pewien, że system :: :: system obsługuje to w ten sposób. (split path, change_extension ...) i jego zachowanie jest oparte na pythonie, jeśli się nie mylę.Zwykle znasz już to rozszerzenie, więc możesz chcieć użyć:
na przykład:
i dostajemy
źródło
basename
jest całkiem.zip
lub.ZIP
. Czy istnieje sposób na zrobienie czegoś takiegobasename $file {.zip,.ZIP}
?Możesz użyć magii rozszerzania parametrów POSIX:
Jest zastrzeżenie, że jeśli twoja nazwa pliku ma formę,
./somefile.tar.gz
toecho ${FILENAME%%.*}
łapczywie usuwa najdłuższe dopasowanie do.
i masz pusty ciąg.(Można to obejść za pomocą zmiennej tymczasowej:
)
Ta strona wyjaśnia więcej.
źródło
cut
nie ma--complement
ised
nie ma-r
.To nie wydaje się działać, jeśli plik nie ma rozszerzenia lub nie ma nazwy pliku. Oto czego używam; wykorzystuje tylko wbudowane i obsługuje więcej (ale nie wszystkie) patologiczne nazwy plików.
A oto kilka przypadków testowych:
źródło
dir="${fullpath:0:${#fullpath} - ${#filename}}"
często widziałemdir="${fullpath%$filename}"
. Łatwiej jest pisać. Nie jestem pewien, czy istnieje jakakolwiek rzeczywista różnica prędkości lub nierówności.which bash
->/bin/bash
; może to twoja dystrybucja?Możesz użyć
basename
.Przykład:
Trzeba zapewnić miejscami nazwę z rozszerzeniem, które zostaną usunięte, jednak jeśli są zawsze wykonując
tar
przy-z
czym wiesz, że rozszerzenie będzie.tar.gz
.To powinno zrobić, co chcesz:
źródło
cd $(basename $1 .tar.gz)
działa na pliki .gz. Ale w pytaniu wspomniałArchive files have several extensions: tar.gz, tat.xz, tar.bz2
działa dobrze, więc możesz po prostu użyć:
Nawiasem mówiąc, polecenia działają w następujący sposób.
Polecenie for
NAME
zastępuje"."
znak, po którym następuje dowolna liczba"."
znaków niebędących znakami aż do końca linii, bez niczego (tzn. Usuwa wszystko od końca"."
do końca linii włącznie). Zasadniczo jest to chciwe podstawienie przy użyciu sztuczek wyrażeń regularnych.Komenda for
EXTENSION
zastępuje dowolną liczbę znaków, po której następuje"."
znak na początku wiersza, bez niczego (tzn. Usuwa wszystko od początku wiersza do ostatniej kropki włącznie). To jest zachłanna zamiana, która jest domyślną akcją.źródło
sed 's,\.[^\.]*$,,'
dla nazwy ised 's,.*\.,., ;t ;g'
dla rozszerzenia (używa poleceń nietypowychtest
iget
poleceń, wraz z typowymsubstitute
poleceniem).Mellen pisze w komentarzu do posta na blogu:
Korzystając z Bash, istnieje również
${file%.*}
możliwość pobrania nazwy pliku bez rozszerzenia i${file##*.}
uzyskania samego rozszerzenia. To jest,Wyjścia:
źródło
Nie trzeba przejmować się
awk
anised
nawetperl
dla tego prostego zadania. Istniejeos.path.splitext()
rozwiązanie zgodne z czystym Bash, które wykorzystuje tylko rozszerzenia parametrów.Wdrożenie referencyjne
Dokumentacja
os.path.splitext(path)
:Kod Python:
Implementacja Bash
Uhonorowanie wiodących okresów
Ignorowanie okresów wiodących
Testy
Oto przypadki testowe dla implementacji Ignorowanie okresów wiodących , które powinny pasować do implementacji referencyjnej Python na każdym wejściu.
Wyniki testów
Wszystkie testy przeszły pomyślnie.
źródło
text.tar.gz
powinna być,text
a rozszerzenie be.tar.gz
os.path.splitext
w Pythonie. To, czy wdrożenie jest rozsądne w przypadku potencjalnie kontrowersyjnych danych wejściowych, to kolejny temat."$root"
)? Co by się stało, gdyby zostały pominięte? (Nie mogłem znaleźć żadnej dokumentacji w tej sprawie.) Również w jaki sposób obsługuje te nazwy plików z nimi*
lub?
w nich?*
I?
nie są wyjątkowe. Tak więc dwie części mojego pytania odpowiadają sobie nawzajem. Czy mam rację, że nie jest to udokumentowane? A może należy to rozumieć z faktu, że cytaty ogólnie wyłączają ekspansję globalną?root="${path#?}";root="${path::1}${root%.*}"
- następnie postępuj tak samo, aby wyodrębnić rozszerzenie.Możesz użyć
cut
polecenia, aby usunąć dwa ostatnie rozszerzenia (".tar.gz"
część):Jak zauważył Clayton Hughes w komentarzu, nie zadziała to w przypadku rzeczywistego przykładu w pytaniu. Jako alternatywę proponuję używać
sed
rozszerzonych wyrażeń regularnych, takich jak:Działa poprzez bezwarunkowe usunięcie dwóch ostatnich (alfanumerycznych) rozszerzeń.
[Zaktualizowany ponownie po komentarzu Andersa Lindahla]
źródło
$
do sprawdzania, czy dopasowane rozszerzenie znajduje się na końcu nazwy pliku. W przeciwnym razie nazwa plikui.like.tar.gz.files.tar.bz2
może spowodować nieoczekiwany wynik.sed
kolejności łańcucha. Nawet z$
na końcu nazwą pliku,mpc-1.0.1.tar.bz2.tar.gz
która usunie oba,.tar.gz
a następnie.tar.bz2
.Oto kilka alternatywnych sugestii (głównie w
awk
), w tym niektóre zaawansowane przypadki użycia, takie jak wyodrębnianie numerów wersji pakietów oprogramowania.Wszystkie przypadki użycia wykorzystują oryginalną pełną ścieżkę jako dane wejściowe, bez zależności od wyników pośrednich.
źródło
Odpowiedź Akceptowane działa dobrze w typowych przypadkach , ale nie w krawędziowych przypadkach , a mianowicie:
extension=${filename##*.}
zwraca nazwę pliku wejściowego zamiast pustego ciągu.extension=${filename##*.}
nie obejmuje początkowej.
, sprzecznej z konwencją..
nie działałoby w przypadku nazw plików bez sufiksu.filename="${filename%.*}"
będzie pustym ciągiem, jeśli nazwa pliku wejściowego zaczyna się od.
i nie zawiera dalszych.
znaków (np..bash_profile
) - w przeciwieństwie do konwencji.---------
Zatem złożoność solidnego rozwiązania, które obejmuje wszystkie przypadki brzegowe, wymaga funkcji - patrz jej definicja poniżej; to może wrócić wszystkie składniki ścieżki .
Przykładowe wywołanie:
Zauważ, że argumenty za ścieżką wejściową są dowolnie wybieranymi nazwami zmiennych pozycyjnych .
Aby pominąć zmienne niebędące przedmiotem zainteresowania, które występują przed tymi, które są, określ
_
(aby użyć zmiennej wyrzucania$_
) lub''
; np. aby wyodrębnić katalog główny i rozszerzenie tylko, użyjsplitPath '/etc/bash.bashrc' _ _ fnameroot extension
.Kod testowy, który wykonuje funkcję:
Oczekiwany wynik - zwróć uwagę na przypadki na krawędziach:
.
( nie uważana za początek sufiksu)/
(końcowe/
jest ignorowany).
jest zwracana jako ścieżka nadrzędna).
prefiks token (tylko ostatni jest uważany za przyrostek):źródło
Najmniejsze i najprostsze rozwiązanie (w jednej linii) to:
źródło
echo
. Ogólnie rzecz biorąc,echo $(command)
jest lepiej napisany po prostu,command
chyba że specjalnie potrzebujesz, aby powłoka wykonała tokenizację białych znaków i interpretację symboli wieloznacznych na wyjściucommand
przed wyświetleniem wyniku. Quiz: jaki jest wynikecho $(echo '*')
(a jeśli tego naprawdę chcesz, naprawdę chceszecho *
.)echo
polecenia. Właśnie użyłem go do zademonstrowania wyniku,foo
który pojawia się w 3. linii jako wynik 2. linii.basename "${file%.*}"
zrobiłbym to samo; używasz podstawienia polecenia, aby przechwycić jego wynik, tylko doecho
tego samego wyniku natychmiast. (Bez cytowania wynik jest nominalnie inny; ale to nie jest istotne, a tym bardziej cecha tutaj.)basename "$file" .txt
Pozwala także uniknąć złożoności podstawiania parametrów.Myślę, że jeśli potrzebujesz tylko nazwy pliku, możesz spróbować:
I to wszystko = D.
źródło
Możesz wymusić wycięcie, aby wyświetlić wszystkie pola i kolejne dodające
-
do numeru pola.Więc jeśli PLIK jest
eth0.pcap.gz
, ROZSZERZENIE będziepcap.gz
Korzystając z tej samej logiki, możesz również pobrać nazwę pliku za pomocą „-” z cięciem w następujący sposób:
Działa to nawet w przypadku nazw plików, które nie mają żadnego rozszerzenia.
źródło
Rozpoznawanie plików magicznych
Oprócz wielu dobrych odpowiedzi na to pytanie dotyczące przepełnienia stosu chciałbym dodać:
W Linuksie i innych systemach uniksowych istnieje magiczne polecenie o nazwie
file
, które wykrywa typ pliku, analizując kilka pierwszych bajtów pliku. To bardzo stare narzędzie, początkowo używane do serwerów wydruku (jeśli nie zostało stworzone dla ... Nie jestem tego pewien).Rozszerzenia standardów można znaleźć w
/etc/mime.types
(na moim pulpicie Debian GNU / Linux. Zobaczman file
iman mime.types
. Być może musisz zainstalowaćfile
narzędzie imime-support
pakiety):Możesz utworzyć grzmotnąćfunkcja określania właściwego rozszerzenia. Jest mała (nie idealna) próbka:
Ta funkcja może ustawić zmienną Bash, której można później użyć:
(Jest to inspirowane prawidłową odpowiedzią @Petesh):
źródło
Ok, więc jeśli dobrze rozumiem, problem polega na tym, jak uzyskać nazwę i pełne rozszerzenie pliku, który ma wiele rozszerzeń, np.
stuff.tar.gz
.To działa dla mnie:
To da ci
stuff
nazwę pliku i.tar.gz
rozszerzenie. Działa dla dowolnej liczby rozszerzeń, w tym 0. Mam nadzieję, że pomoże to każdemu, kto ma ten sam problem =)źródło
os.path.splitext
, czego chce PO) jest('stuff.tar', '.gz')
.Używam następującego skryptu
źródło
Obsługuje wiele kropek i spacji w nazwie pliku, jednak jeśli nie ma rozszerzenia, zwraca nazwę pliku. Łatwo to jednak sprawdzić; po prostu sprawdź, czy nazwa pliku i rozszerzenie są takie same.
Oczywiście ta metoda nie działa w przypadku plików .tar.gz. Można to jednak rozwiązać w dwuetapowym procesie. Jeśli rozszerzenie to gz, sprawdź ponownie, czy jest również rozszerzenie tar.
źródło
Jak wyodrębnić nazwę pliku i rozszerzenie w rybach :
Ostrzeżenia: Dzieli na ostatnią kropkę, co działa dobrze w przypadku nazw plików z kropkami, ale nie jest dobre w przypadku rozszerzeń z kropkami. Zobacz przykład poniżej.
Stosowanie:
Prawdopodobnie są na to lepsze sposoby. Edytuj swoją odpowiedź, aby ją poprawić.
Jeśli istnieje ograniczony zestaw rozszerzeń, z którymi będziesz mieć do czynienia i znasz je wszystkie, spróbuj tego:
To nie ma zastrzeżenia jako pierwszego przykładu, ale musisz poradzić sobie z każdą sprawą, więc może być bardziej nużąca w zależności od tego, ile rozszerzeń możesz się spodziewać.
źródło
Oto kod z AWK . Można to zrobić prościej. Ale nie jestem dobry w AWK.
źródło
split()
.awk -F / '{ n=split($2, a, "."); print a[n] }' uses
/ `jako ogranicznik najwyższego poziomu, ale następnie dzieli drugie pola.
i drukuje ostatni element z nowej tablicy.Po prostu użyj
${parameter%word}
W Twoim przypadku:
Jeśli chcesz to przetestować, wykonaj następujące czynności i po prostu usuń rozszerzenie:
źródło
=
znaków nie powinno być odstępów .Opierając się na odpowiedzi z Petesh , jeśli potrzebna jest tylko nazwa pliku, zarówno ścieżkę, jak i rozszerzenie można usunąć w jednym wierszu,
źródło
filename="$(basename "${fullname%.*}")"
basename
jest opcjonalny, ale określa rozszerzenie do usunięcia. Podstawienie może być nadal przydatne, ale być możebasename
nie jest, ponieważ wszystkie te podstawienia można wykonać za pomocą wbudowanych powłok.Oparte w dużej mierze na doskonałej @ mklement0 i pełne losowych, przydatnych bashism - a także innych odpowiedzi na to / inne pytania / „ten cholerny internet” ... podsumowałem to wszystko trochę, nieco bardziej zrozumiale, funkcja wielokrotnego użytku dla mojego (lub twojego),
.bash_profile
który dba o to, co (uważam) powinno być bardziej niezawodną wersjądirname
/basename
/ co masz ...Przykłady użycia ...
źródło
$IFS
w ogóle nie polegasz (a jeśli tak, możesz użyć golocal
do zlokalizowania efektu ustawienia). - Lepiej używaćlocal
zmiennych. - Twój komunikat o błędzie powinien zostać wyświetlonystderr
, a niestdout
(użyj1>&2
), i powinieneś zwrócić niezerowy kod wyjścia. - Lepiej zmienić nazwęfullname
nabasename
(pierwsza sugeruje ścieżkę z komponentami katalogu). -name
bezwarunkowo dołącza.
(kropkę), nawet jeśli oryginał nie miał. Możesz po prostu użyćbasename
narzędzia, ale pamiętaj, że ignoruje ono zakończenie/
.Prosta odpowiedź:
Aby rozwinąć na POSIX zmiennych odpowiedzi należy pamiętać, że można zrobić więcej ciekawych wzorów. Dlatego w przypadku opisanym tutaj możesz po prostu to zrobić:
To odetnie ostatnie wystąpienie .tar. <coś> .
Mówiąc bardziej ogólnie, jeśli chcesz usunąć ostatnie wystąpienie. <coś> . <something-else> następnie
powinien działać dobrze.
Link do powyższej odpowiedzi wydaje się martwy. Oto świetne wyjaśnienie wielu manipulacji ciągami, które możesz wykonać bezpośrednio w Bash, z TLDP .
źródło
Jeśli chcesz także zezwolić na puste rozszerzenia, jest to najkrótsza z możliwych:
Wyjaśnienie pierwszej linii: Pasuje do PATH.EXT lub INNE i zastępuje ją EXT. Jeśli dopasowano WSZYSTKIE, grupa ext nie jest przechwytywana.
źródło
To jedyny, który działał dla mnie:
Można tego również użyć w interpolacji ciągów, ale niestety trzeba to
base
wcześniej ustawić .źródło
Oto algorytm, którego użyłem do znalezienia nazwy i rozszerzenia pliku, kiedy napisałem skrypt Bash, aby uczynić nazwy unikalnymi, gdy nazwy są w konflikcie ze względu na wielkość liter.
Uruchomienie testowe
FYI: Kompletny program transliteracji i więcej przypadków testowych można znaleźć tutaj: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
źródło
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
Korzystając z przykładowego pliku
/Users/Jonathan/Scripts/bash/MyScript.sh
, ten kod:spowoduje
${ME}
bycieMyScript
i${MY_EXT}
bycie.sh
:Scenariusz:
Niektóre testy:
źródło
basename
może być przesadą.Z powyższych odpowiedzi, najkrótszy oneliner naśladuje Pythona
zakładając, że plik naprawdę ma rozszerzenie, jest
źródło
EXT
więc są to żółwie na całej długości. (Ponadto należy unikać