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.

mimicocotopus
źródło
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
Rohan
źródło
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
Chepner
źródło
6
tutaj wyjaśnienie polecenia: gnu.org/software/bash/manual/html_node/…
Carlos Robles
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
117

Jeśli znasz rozszerzenie, możesz użyć basename

$ basename /home/jsmith/base.wiki .wiki
base
Steven Penny
źródło
Jak mogę usunąć tylko .wikii skończyć z /home/jsmith/base?
Gabriel Staples
Aktualizacja: odpowiedź: zobacz stackoverflow.com/a/32584935/4561887 . Działa świetnie!
Gabriel Staples
20

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

echo $filename | rev | cut -f 2- -d '.' | rev
manish_s
źródło
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.

Raghwendra
źródło
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.

C. Paul Bond
źródło
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%.*}")'
Alexey K.
źródło
1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

wyjścia:

program
Boo Boo
źródło
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)
MadMage
źródło
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
$> 
FanDeLaU
źródło