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.