Odpowiedzi:
basename
z jądra GNU mogą pomóc w wykonaniu tej pracy:
$ basename /root/video.mp4
video.mp4
Jeśli znasz już rozszerzenie pliku, możesz go wywołać, basename
używając składni basename NAME [SUFFIX]
, aby go usunąć:
$ basename /root/video.mp4 .mp4
video
Inną opcją byłoby wycinanie wszystkiego po ostatniej kropce za pomocą sed
:
$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
Użyj dowolnego z poniższych sposobów:
out_file="${in_file##*/}"
out_file="$(basename $in_file)"
out_file="$(echo $in_file | sed 's=.*/==')"
out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"
ps. Otrzymujesz ten sam ciąg znaków, ponieważ w swojej instrukcji \(.*\.\)
pasuje do ciągu od początku do kropki ( /root/video.
), a następnie dodajesz ręcznie, .mp4
który jest taki sam jak w swoim oryginalnym ciągu. Zamiast tego powinieneś użyć s=.*\([^/]*\)=\1=
.
Aktualizacja: (Pierwszy jest teraz naprawiony)
Aby uzyskać jedyną nazwę pliku bez rozszerzenia, możesz:
out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/')"
out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"
out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
my.file.tar.gz
.
sed
i awk
. Naprawiony. Dziękuję Ci.
Jedną z podstaw używania wyrażenia regularnego jest to, że przy określaniu symbolu wieloznacznego wzorce są z natury chciwe. Chociaż odpowiedź zaproponowana przez @uloBasEI jest z pewnością działającą odpowiedzią, wymaga także użycia polecenia basename. Oryginalne pytanie z @Shixons wymaga rozwiązania wykorzystującego tylko sed.
Przed kontynuowaniem zawsze warto wiedzieć, która wersja sed jest celem. Zakładam, że BSD (jak dostarczane z OSX).
Po pierwsze, wzór zaproponowany w pierwotnym pytaniu nie działa, ponieważ przechwytuje wszystko od początku ciągu wejściowego do ostatniej kropki włącznie. Bez kotwic wyszukiwanie to pochłonie wszystko od lewej do prawej. Dopasowany wzorzec „/ 1” to zatem wszystko do ostatniej kropki włącznie. Nawet nazwa pliku z wieloma kropkami zostanie połknięta w całości. Wcale nie pożądany wynik.
Pierwszym krokiem jest ustanowienie strategii identyfikacji wzorców. Tutaj chcesz pozbyć się wszystkiego po lewej stronie nazwy pliku (zajmiemy się rozszerzeniem później):
out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"
Wyszukiwanie pasuje do początku ciągu. Dopasowuje wzorzec „/.*” zero lub więcej razy i usuwa wszystko później. Dopasowane wzory drukujemy za pomocą „\ 1”. Nie szukamy globalnie; szukamy od początku łańcucha, określając ^ anchor.
Uzyskujemy lepszą przejrzystość, włączając opcję „-E”, dzięki czemu nie musimy uciekać od nawiasów:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"
Więc teraz mamy część po lewej stronie. Dodajmy część po prawej stronie. Zauważ, że musimy zachować lewą część jako wzór, ponieważ w ten sposób możemy określić, że pojawia się zero lub więcej razy. Teraz dodajemy wzór dla części po prawej:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"
Drukujemy tylko drugie dopasowanie, odrzucając w ten sposób wszystko oprócz nazwy pliku. Ale nadal musimy usunąć rozszerzenie nazwy pliku.
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"
„$” Na końcu jest opcjonalne.
Na koniec, aby dodać nowe rozszerzenie, które właśnie zmieniacie:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"
Dodatkową optymalizacją jest uczynienie pierwszego ukośnika do przodu opcjonalnym do obsługi ścieżek względnych:
out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"
Natknąłem się na to pytanie, będąc leniwym, szukając wzoru sed, który zastąpiłby basename . Pracuję na systemie okrojonym, w którym nie zainstalowano tego polecenia.
sed 's/\.[^.]*$//'
jak trzeba, nie powiedzie się na (ukryty).filename
i.
i..
katalogi