Jak uzyskać tylko nazwę pliku za pomocą sed

17

Jak mogę uzyskać tylko nazwę pliku za pomocą sed? Mam to

out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")

Ale ja też dostaję ścieżkę /root/video.mp4i chcę tylko video.mp4.

Shixons
źródło

Odpowiedzi:

26

basenamez 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ć, basenameuż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
uloBasEI
źródło
3
Korzystanie sed 's/\.[^.]*$//'jak trzeba, nie powiedzie się na (ukryty) .filenamei .i ..katalogi
Peter.O
9

Najłatwiejszym rozwiązaniem jest usunięcie wszystkiego do ostatniego pojawienia się /:

echo /root/video.mp4 | sed 's/.*\///'

stokrotka
źródło
5

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, .mp4któ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 }'
wysypka
źródło
Ale przy każdej z tych metod otrzymuję nazwę pliku w formacie i muszę tylko nazwę pliku i ręcznie ustawić nowy format.
Shixons,
Ach, to ma sens. Zaktualizowałem swoją odpowiedź.
pędzi
@rush: Pojawią się przypadki krawędzi, np. dla pliku o nazwie my.file.tar.gz.
donotings udane
@donothingsnie powiodło się brakujący symbol kropki w ostatnim sedi awk. Naprawiony. Dziękuję Ci.
pędzi
4

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.

Markeissler
źródło