Jak mogę usunąć rozszerzenie nazwy pliku w skrypcie powłoki?


144

Co jest nie tak z następującym kodem?

name='$filename | cut -f1 -d'.''

Tak jak jest, otrzymuję dosłowny ciąg $filename | cut -f1 -d'.', ale jeśli usunę cudzysłowy, nic nie otrzymam. Tymczasem piszę

"test.exe" | cut -f1 -d'.'

w powłoce daje mi wyjście chcę, test. Wiem już, że $filenamezostała przypisana odpowiednia wartość. Chcę przypisać zmiennej nazwę pliku bez rozszerzenia.


6
basename $filename .exezrobiłby to samo. To przy założeniu, że zawsze wiesz, jakie rozszerzenie chcesz usunąć.
— mpe

6
@mpe, masz na myśli basename "$filename" .exe. W przeciwnym razie nazwy plików ze spacjami byłyby złą wiadomością.
— Charles Duffy

Odpowiedzi:


113

Powinieneś używać składni podstawiania poleceń,$(command) gdy chcesz wykonać polecenie w skrypcie / poleceniu.

Więc twoja linia byłaby

name=$(echo "$filename" | cut -f 1 -d '.')

Objaśnienie kodu:

  1. echopobierz wartość zmiennej $filenamei wyślij ją na standardowe wyjście
  2. Następnie pobieramy dane wyjściowe i przesyłamy je do cutpolecenia
  3. cutUżyje. jako separator (znany również jako separator) do cięcia ciągu na segmenty i przez -fwybranie segmentu, który chcemy mieć na wyjściu
  4. Następnie $()podstawienie polecenia spowoduje uzyskanie wyniku i zwrócenie jego wartości
  5. Zwrócona wartość zostanie przypisana do nazwanej zmiennej name

Zauważ, że to daje część zmiennej do pierwszego okresu .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Dzięki. Zauważyłem również, że muszę użyć polecenia echo. name =echo $filename | cut -f1 -d'.'
— mimicocotopus

17
Backticks są przestarzałe przez POSIX, $()jest preferowane.
— jordanm

3
Rozwidlenie i orurowanie w celu uzyskania kilku znaków to najgorsze rozwiązanie, jakie można sobie wyobrazić.
— Jens

39
Problem z tą odpowiedzią polega na tym, że zakłada się, że ciąg wejściowy ma TYLKO jedną kropkę ... @chepner poniżej ma znacznie lepsze rozwiązanie ... name = $ {filename%. *}
— Scott Stensland

4
Ta odpowiedź jest charakterystyczna dla początkujących i nie powinna być rozpowszechniana. Użyj wbudowanego mechanizmu opisanego w odpowiedzi
— Chepnera

258

Możesz także użyć rozszerzenia parametrów:

$ filename=foo.txt
$ echo "${filename%.*}"
foo


1
I tutaj miałem użyć echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Dzięki.
— user208145

1
Czy to działa z plikami z wieloma rozszerzeniami, takimi jak image.png.gz?
— Hawker65

11
%.*usunie tylko ostatnie rozszerzenie; jeśli chcesz usunąć wszystkie rozszerzenia, użyj %%.*.
— chepner

1
Wydaje się, że działa to znacznie lepiej niż przyjęta odpowiedź. Zmniejsza tylko ostatnie rozszerzenie, jeśli plik ma więcej niż jedną kropkę, podczas gdy cut usuwa wszystko po pierwszej kropce.
— Dale Anderson


20

Jeśli nazwa pliku zawiera kropkę (inną niż rozszerzenie), użyj tego:

echo $filename | rev | cut -f 2- -d '.' | rev

1
Zapomniałem o średnich obrotach, ale kiedy je zobaczyłem, było świetnie!
— najwyższy Pooba,

Jest jeszcze lepiej z -sopcjącut , która zwraca pusty ciąg, gdy nazwa pliku nie zawiera kropki.
— Hibou57

1
Moim zdaniem powinna to być akceptowana odpowiedź, ponieważ działa na ścieżce z kropkami, z ukrytymi plikami zaczynającymi się od kropki, a nawet z plikiem z wieloma rozszerzeniami.
— Tim Krief

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

użyj tego, co chcesz. Tutaj zakładam, że ostatnia .(kropka), po której następuje tekst, to rozszerzenie.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

wyjścia:

/tmp/foo.bar.gz /tmp/foo.bar

Zwróć uwagę, że usuwane jest tylko ostatnie rozszerzenie.


3

Moja rekomendacja to użycie basename.
Jest to domyślnie w Ubuntu, wizualnie prosty kod i radzi sobie z większością przypadków.

Oto kilka przypadków podrzędnych zajmujących się spacjami i rozszerzeniem wielopunktowym / podrzędnym:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Zwykle pozbywa się rozszerzenia od pierwszego ., ale zawodzi na naszej ..drodze

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Oto ważna uwaga:

Użyłem podwójnych cudzysłowów wewnątrz podwójnych cudzysłowów, aby poradzić sobie ze spacjami. Pojedynczy cudzysłów nie przejdzie z powodu wysłania SMS-a. Bash jest nietypowy i ze względu na rozszerzenie czyta „drugie” pierwsze „cudzysłowy”.

Jednak nadal musisz o tym pomyśleć .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

nie oczekiwany wynik. Aby tak się stało, użyj $HOMElub, /home/user_path/
ponieważ ponownie bash jest „nietypowy” i nie rozwijaj „~” (wyszukaj bash BashPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

wyjścia:

program

Czym różni się to od odpowiedzi udzielonej przez Stevena Penny 3 lata temu?
— gniourf_gniourf

1

Jak zauważył Hawker65 w komentarzu do odpowiedzi Chepnera, najczęściej głosowane rozwiązanie nie obejmuje ani wielu rozszerzeń (takich jak nazwa_pliku.tar.gz), ani kropek w pozostałej części ścieżki (takiej jak ta ścieżka / z .dots / in.path.name). Możliwe rozwiązanie to:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Ten usuwa „tar.gz”, wybierając znaki przed pierwszym wystąpieniem kropki w nazwie pliku, nie licząc ścieżki. Prawdopodobnie nie chce się w ten sposób usuwać rozszerzeń.
— Frotz

0

Dwa problemy z Twoim kodem:

  1. Użyłeś '(tick) zamiast' (back tick), aby otoczyć polecenia, które generują ciąg, który chcesz przechowywać w zmiennej.
  2. Nie „echo” zmiennej „$ filename” do potoku w poleceniu „cut”.

Zmieniłbym twój kod na "name =` echo $ filename | cut -f 1 -d '.' `", jak pokazano poniżej (ponownie zwróć uwagę na tylne znaczniki otaczające definicję zmiennej nazwy):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
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.