Soru Bash'de dosya adı ve uzantı ayıkla


Dosya adını (uzantısız) ve uzantıyı ayrı ayrı almak istiyorum.

Şimdiye kadar bulduğum en iyi çözüm:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

Bu yanlış, çünkü dosya adı birden çok içeriyorsa çalışmıyor . karakter. Eğer diyelim ki a.b.jsdüşünecek a ve b.js, yerine a.b ve js.

Python ile kolayca yapılabilir.

file, ext = os.path.splitext(path)

ama mümkünse sadece bunun için bir Python yorumcusu yakmamayı tercih ederim.

Daha iyi bir fikir var mı?


1620
2018-06-08 14:00


Menşei


Bu soru Bu bash tekniğini ve diğer birkaç ilgili şeyi açıklar. - jjclarkson
Aşağıdaki harika yanıtları uygularken, burada gösterdiğim gibi değişkeninizi yapıştırmayın. Yanlış:  extension="{$filename##*.}" bir süredir yaptığım gibi! Taşı $ kıvırcıkların dışında: Sağ:  extension="${filename##*.}" - Chris K
Bu açıkça önemsiz bir sorundur ve benim için aşağıdaki cevapların tamamen doğru olup olmadığını söylemek zor. Bu şaşırtıcı bir şey (ba) sh (dahili cevaplar kullanarak fonksiyonu uygulamak gibi görünüyor) yerleşik bir operasyon değildir. Python’u kullanmaya karar verdim os.path.splitext Yukarıdaki gibi ... - Peter Gibson
Anlaştık - aynı yorum. Sayısız kullanım için komut satırı anahtarlarını alan derlenmiş / alınabilir derlenmiş bir yardımcı program mevcut değil - örn. uzantısı yerine almak veya sonuncu sınırlayıcılar veya ikili başlıkta dosya türünü kontrol etme. .? - Alex Hall
Gibi uzantı temsil etmek doğa bir dosya var, bir sihirli kendi doğasını ve teklifini bildirmek için kontrol dosyası standart uzatma. görmek cevabım - F. Hauri


Cevaplar:


Öncelikle, yol adı olmadan dosya adı alın:

filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

Alternatif olarak, '.' Yerine yolun son '/' üzerine odaklanabilirsiniz. önceden kestirilemeyen dosya uzantılarınız olsa bile çalışmanız gerekir:

filename="${fullfile##*/}"

2815
2018-06-08 14:05



Çıkış yapmak gnu.org/software/bash/manual/html_node/... Tam özellik kümesi için. - D.Shawley
"$ Fullfile" 'a bazı alıntılar ekleyin ya da dosya ismini kırma riski taşıyacaksınız. - lhunath
Heck, hatta filename = "$ {fullfile ## * /}" yazabilir ve fazladan çağırabilirsin. basename - ephemient
Dosyada bir uzantı yoksa bu "çözüm" işe yaramaz - bunun yerine, tüm dosya adı çıktı, uzantıları olmayan dosyaların her yerde olduğunu düşünürsek oldukça kötü. - nccc
Uzantısız dosya adları ile uğraşmak için düzeltme: extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo ''). Bir uzantı varsa olduğu mevcut, ilk dahil olmak üzere iade edilecektir ., Örneğin., .txt. - mklement0


~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

Daha fazla bilgi için bkz. kabuk parametresi genişletme Bash kılavuzunda.


503
2018-06-08 14:05



Siz (belki de istemeden), dosya adının "uzantı" kısmının içinde 2 noktaya sahip olmanız durumunda ne yapmanız gerektiği ile ilgili mükemmel soruyu ortaya koyarsınız. .Gm.gz ... Bu sorunu hiç düşünmedim ve sanırım ön taraftaki olası geçerli dosya uzantılarını bilmeden çözülebilir değil. - rmeador
Neden çözülebilir değil? Benim örneğimde, dosyanın içerdiği düşünülmelidir iki uzantılar, iki nokta ile bir uzantı değil. Her iki uzantıyı ayrı ayrı ele alırsınız. - Juliano
Sözcüksel olarak çözülemez, dosya türünü kontrol etmeniz gerekir. Bir oyunun olduğunu düşünün. dinosaurs.in.tar ve sen onu gzipledin dinosaurs.in.tar.gz :) - porges
Tam yollardan geçiyorsanız bu durum daha da karmaşıklaşır. Benimkilerden bir tanesi vardı. Yolun ortasında bir dizinde, ancak dosya adında hiçbiri yok. "A / b.c / d / e / dosyaadı" örneği ".c / d / e / dosyaadı" olarak işaretlenecekti - Walt Sellers
açıkça hayır x.tar.gzuzantısı gz ve dosya adı x.tar bu kadar. Çift uzantı diye bir şey yoktur. Ben eminim boost :: dosya sistemi bu şekilde işler. (split path, change_extension ...) ve eğer yanılmıyorsam davranışları python'a dayanıyor. - v.oddou


Genellikle uzantıyı zaten biliyorsunuz, bu yüzden kullanmak isteyebilirsiniz:

basename filename .extension

Örneğin:

basename /path/to/dir/filename.txt .txt

ve aldık

filename

281
2017-10-19 10:56



Bu ikinci argüman basename oldukça göz açıcı, ty tür efendim / hanımefendi :) - akaIDIOT
Ve bu tekniği kullanarak uzantısı nasıl çıkarılır? ;) Bekle! Aslında bunu önceden bilmiyoruz. - Tomasz Gandor
İle biten bir sıkıştırılmış dizininiz olduğunu varsayalım .zip veya .ZIP. Gibi bir şey yapmanın bir yolu var mı basename $file {.zip,.ZIP}? - Dennis
Bu sadece OP'lerin sorusunun bir kısmını yanıtlarken, google'a yazdığım soruyu yanıtlıyor. :-) Çok kaygan! - sudo make install


POSIX değişkenlerinin büyüsünü kullanabilirsiniz:

bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar

Dosya adınız formdaysa, bir uyarı vardır. ./somefile.tar.gz sonra echo ${FILENAME%%.*} açgözlülükle en uzun maçı . ve boş dizinin var.

(Geçici bir değişkeni kullanarak bu konuda çalışabilirsiniz:

FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}

)


Bu yer daha fazla açıklar.

${variable%pattern}
  Trim the shortest match from the end
${variable##pattern}
  Trim the longest match from the beginning
${variable%%pattern}
  Trim the longest match from the end
${variable#pattern}
  Trim the shortest match from the beginning

125
2018-02-05 09:09



Joachim'in cevabından çok daha basit ama her zaman POSIX değişken ikamesine bakmak zorundayım. Ayrıca, bu Max OSX üzerinde çalışır cut sahip değil --complement ve sed sahip değil -r. - jwadsack


Dosyada uzantı yoksa veya dosya adı yoksa, bu işe yaramaz. İşte benim kullandığım şey; sadece yerleşik kullanır ve daha fazla (ama hepsi değil) patolojik dosya isimlerini kullanır.

#!/bin/bash
for fullpath in "$@"
do
    filename="${fullpath##*/}"                      # Strip longest match of */ from start
    dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
    if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
        base=".$ext"
        ext=""
    fi

    echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
done

Ve bazı testisler:

$ basename-and-extension.sh / / home / me / / home / me / dosya /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden / home / me / .hidden.tar / ev / ben / ..
/:
    dir = "/"
    base = ""
    ext = ""
/ Home / me /:
    dir = "/ home / me /"
    base = ""
    ext = ""
/ Home / me / dosya:
    dir = "/ home / me /"
    base = "dosya"
    ext = ""
/home/me/file.tar:
    dir = "/ home / me /"
    base = "dosya"
    ext = "tar"
/home/me/file.tar.gz:
    dir = "/ home / me /"
    base = "dosya.tar"
    ext = "gz"
/home/me/.hidden:
    dir = "/ home / me /"
    base = ".hidden"
    ext = ""
/home/me/.hidden.tar:
    dir = "/ home / me /"
    base = ".hidden"
    ext = "tar"
/ Home / me / ..:
    dir = "/ home / me /"
    base = ".."
    ext = ""
.:
    dir = ""
    base = "."
    ext = ""

65
2017-09-10 05:17



Yerine dir="${fullpath:0:${#fullpath} - ${#filename}}" Sık sık gördüm dir="${fullpath%$filename}". Yazmak daha basit. Herhangi bir gerçek hız farkı veya gotchas olduğundan emin değil. - dubiousjim
Bu neredeyse her zaman yanlış olan #! / Bin / bash kullanır. Mümkünse #! / Bin / sh, yoksa #! / Usr / bin / env bash tercih et. - Good Person
@ İyi İnsan: Neredeyse her zaman yanlış olduğunu bilmiyorum: which bash -> /bin/bash ; belki senin dağıtımın mı? - vol7ron
@ vol7ron - birçok distros bash / usr / local / bin / bash dizinindedir. OSX'te birçok kişi / opt / local / bin / bash dosyasına güncellenmiş bir bash yükler. Gibi / bin / bash yanlış ve env onu bulmak için kullanmalısınız. Daha da iyisi / bin / sh ve POSIX yapılarını kullanmaktır. Solaris dışında bu bir POSIX kabuğudur. - Good Person
@GoodPerson ama eğer bash ile daha rahatsanız, neden sh kullanıyorsunuz? Söylemenin sebebi, Perl'i neden kullanabiliyor? - vol7ron


Kullanabilirsiniz basename.

Örnek:

$ basename foo-bar.tar.gz .tar.gz
foo-bar

Eğer her zaman yürütüyorsanız, kaldırılacak uzantı ile basename sağlamanız gerekir. tar ile -z o zaman uzantının olacağını biliyorsunuz .tar.gz.

Bu istediğini yapmalı:

tar -zxvf $1
cd $(basename $1 .tar.gz)

40
2018-02-05 08:50



sanırım cd $(basename $1 .tar.gz) .gz dosyaları için çalışır. Fakat söz konusu olan Archive files have several extensions: tar.gz, tat.xz, tar.bz2 - SS Hegde
Tomi Po, 2 yıl önce aynı şeyleri paylaştı. - Blauhirn
Merhaba Blauhirn, bu eski bir soru. Bence tarihlere bir şey oldu. Soruyu sorduktan kısa bir süre sonra soruyu yanıtlamayı çoktan hatırlıyorum ve burada sadece birkaç cevap daha var. Soru başka biriyle birleştirilmiş olabilir mi, SO bunu yapar mı? - Bjarke Freund-Hansen
Evet doğru hatırlıyorum. Aslında bu soruya cevap veriyorum stackoverflow.com/questions/14703318/... Aynı günde 2 yıl sonra buna birleştirilmişti. Cevabım bu şekilde taşındığında, yinelenen bir cevap için pek de suçlanmam. - Bjarke Freund-Hansen


Mellen bir blog gönderisine yorumda yazıyor:

Bash'i kullanmak da var. ${file%.*} dosya adını uzantı olmadan almak ve ${file##*.} uzantısı tek başına almak için. Yani,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

Çıktılar:

filename: thisfile
extension: txt

24
2017-07-21 10:24



Bu sözdizimi tam olarak nasıl çalışır? - syntagma
@REACHUS: Gör gnu.org/software/bash/manual/html_node/... - mklement0


pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

iyi çalışıyor, bu yüzden sadece kullanabilirsiniz:

pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js

Komutlar, bu arada, aşağıdaki gibi çalışır.

İçin komut NAME yerine geçen "." karakter olmayan herhangi bir sayı ile takip"." satırın sonuna kadar karakterler, hiçbir şey olmadan (yani, her şeyi sondan kaldırır "."hattın sonuna kadar, dahil). Bu temelde düzenli ifadeler kullanarak açgözlü olmayan bir ikame.

İçin komut EXTENSION herhangi bir sayıda karakterle değiştirir ve ardından "." satırın başlangıcındaki karakter, hiçbir şey olmadan (yani, çizginin başlangıcından son noktaya kadar her şeyi kaldırır). Bu varsayılan eylem olan açgözlü bir ikame.


24
2018-06-08 14:14





Kullanabilirsiniz cut Son iki uzantıyı kaldırma komutu ( ".tar.gz" Bölüm):

$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo

Clayton Hughes'un bir yorumda belirttiği gibi, bu sorudaki gerçek örnek için işe yaramayacaktır. Yani bir alternatif olarak kullanmayı düşünüyorum sed genişletilmiş düzenli ifadelerle, bunun gibi:

$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1

Son iki (alfa sayısal) uzantıyı koşulsuz olarak kaldırarak çalışır.

[Anders Lindahl'ın yorumundan sonra tekrar güncellendi]


22
2018-02-05 08:53



Bu, yalnızca dosya adı / yolun başka bir nokta içermediği durumlarda çalışır: echo "mpc-1.0.1.tar.gz" | kes -d '. --complement -f2- "mpc-1" üretir (sınırlamadan sonra sadece ilk 2 alan). - Clayton Hughes
@ClaytonHughes Haklısın ve daha iyi test etmeliydim. Başka bir çözüm eklendi. - Some programmer dude
Sed ifadeleri kullanmalı $ Eşleşen uzantının dosya adının sonunda olduğunu kontrol etmek için. Aksi takdirde, bir dosya adı gibi i.like.tar.gz.files.tar.bz2 beklenmedik sonuç doğurabilir. - Anders Lindahl
@AndersLindahl Eğer uzantıların sırası, sed zincir düzeni. Bile $ sonunda bir dosya adı gibi mpc-1.0.1.tar.bz2.tar.gz ikisini de kaldıracak .tar.gz ve sonra .tar.bz2. - Some programmer dude