web-dev-qa-db-ja.com

Bashでファイル名と拡張子を抽出する

ファイル名(拡張子なし)と拡張子を別々に取得したい。

私がこれまでに見つけた最良の解決策は、次のとおりです。

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

ファイル名に複数の.文字が含まれていると機能しないため、これは間違っています。仮に、私がa.b.jsを持っているとしたら、それはb.jsaの代わりにjsa.bを考慮します。

それはPythonで簡単に行うことができます

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

しかし、可能であれば、私はこれのためだけにPythonインタプリタを起動したくないと思います。

もっと良いアイデアはありますか?

1821
ibz

まず、パスなしでファイル名を取得します。

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

あるいは、「。」ではなく、パスの最後の「/」に注目することもできます。あなたが予想できないファイル拡張子を持っていてもこれは動作するはずです:

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

あなたはドキュメンテーションをチェックしたいかもしれません:

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

詳しくは、Bashマニュアルの シェルパラメーターの展開 を参照してください。

568
Juliano

通常、あなたはすでに拡張子を知っているので、あなたは使いたいかもしれません:

basename filename .extension

例えば:

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

そして私達は得る

filename
336
Tomi Po

POSIX変数の魔法を使うことができます。

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

あなたのファイル名が./somefile.tar.gzの形式である場合、echo ${FILENAME%%.*}.との最長一致を貪欲に削除し、空の文字列を持つことになるという警告があります。

(あなたは一時的な変数でそれを回避することができます:

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


これは site がさらに説明しています。

${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
132
sotapme

ファイルに拡張子やファイル名がない場合はうまくいきません。これが私が使っているものです。それは組み込みのものだけを使用し、より多くの(しかし全てではない)病理学的ファイル名を扱う。

#!/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

そしてここにいくつかのテストケースがあります:

 $ basename-and-extension.sh// home/me// home/me/file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden /home/me/.hidden.tar/home/me/.。
 /:
 dir = "/"
 base =" "
 ext = "" 
/home/me /: 
 dir = "/home/me/" 
 base =" "
 ext =" "
 /home/me/file:
 dir = "/home/me/"
 base ="ファイル "
 ext =" "
/home/me/file.tar:
 dir = "/home/me/"
 base ="ファイル "
 ext =" tar "
/home/me/file。 tar.gz:
 dir = "/home/me/"
 base =" file.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 = "" 
67
Doctor J

basename を使用できます。

例:

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

削除する拡張子を付けてbasenameを指定する必要がありますが、tar-zで常に実行している場合は、拡張子が.tar.gzになることがわかります。

これはあなたが望むことをするはずです:

tar -zxvf $1
cd $(basename $1 .tar.gz)
42
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

うまく動作するので、あなたはただ使うことができます:

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

ちなみに、コマンドは次のように機能します。

NAMEのコマンドは、"."文字とそれに続く行末までの任意の数の非"."文字を何も置き換えずに置き換えます(つまり、最後の"."から行末までのすべてを削除します)。これは基本的に正規表現のトリックを使った欲張りではない代用です。

EXTENSIONのコマンドは、行の先頭に"."の文字が続く任意の数の文字を何も置き換えずに置き換えます(つまり、行の先頭から最後のドットまでのすべてを削除します)。これはデフォルトのアクションである欲張りな代用です。

29
paxdiablo

Mellenはブログ記事にコメントを書いている。

Bashを使用すると、拡張子なしのファイル名を取得するための${file%.*}と、拡張子のみを取得するための${file##*.}もあります。あれは、

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

出力:

filename: thisfile
extension: txt
26
Kebabbert

最後の2つの拡張子(".tar.gz"部分)を削除するには、 cut コマンドを使用します。

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

コメントでClayton Hughesによって述べられたように、これは質問の実際の例のために働かないでしょう。そのため、代替手段として、次のように拡張正規表現でsedを使用することを提案します。

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

それは無条件に最後の2つの(英数字)拡張子を削除することによって機能します。

[Anders Lindahlからのコメントの後に再び更新されました]

25

この単純な作業のためにawksed、さらにはPerlに煩わされる必要はありません。パラメータ展開のみを使用する、純粋なBash、os.path.splitext()互換ソリューションがあります。

リファレンス実装

os.path.splitext(path) :のドキュメント

(root, ext)、および ext が空、またはピリオドで始まり、最大で1つのピリオドを含むように、パス名pathをroot + ext == pathのペアに分割します。ベース名の先頭のピリオドは無視されます。 splitext('.cshrc')('.cshrc', '')を返します。

Pythonコード:

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

Bashの実装

リーディングピリオドを尊重する

root="${path%.*}"
ext="${path#"$root"}"

先行期間を無視する

root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"

テスト

これは 先行ピリオドを無視する 実装のテストケースです。これはすべての入力でPythonリファレンス実装と一致するはずです。

|---------------|-----------|-------|
|path           |root       |ext    |
|---------------|-----------|-------|
|' .txt'        |' '        |'.txt' |
|' .txt.txt'    |' .txt'    |'.txt' |
|' txt'         |' txt'     |''     |
|'*.txt.txt'    |'*.txt'    |'.txt' |
|'.cshrc'       |'.cshrc'   |''     |
|'.txt'         |'.txt'     |''     |
|'?.txt.txt'    |'?.txt'    |'.txt' |
|'\n.txt.txt'   |'\n.txt'   |'.txt' |
|'\t.txt.txt'   |'\t.txt'   |'.txt' |
|'a b.txt.txt'  |'a b.txt'  |'.txt' |
|'a*b.txt.txt'  |'a*b.txt'  |'.txt' |
|'a?b.txt.txt'  |'a?b.txt'  |'.txt' |
|'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
|'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
|'txt'          |'txt'      |''     |
|'txt.pdf'      |'txt'      |'.pdf' |
|'txt.tar.gz'   |'txt.tar'  |'.gz'  |
|'txt.txt'      |'txt'      |'.txt' |
|---------------|-----------|-------|

試験結果

すべてのテストに合格しました。

23
Cyker

ソフトウェアパッケージのバージョン番号の抽出などの高度なユースケースを含む、いくつかの代替提案(主にawk)があります。

f='/path/to/complex/file.1.0.1.tar.gz'

# Filename : 'file.1.0.x.tar.gz'
    echo "$f" | awk -F'/' '{print $NF}'

# Extension (last): 'gz'
    echo "$f" | awk -F'[.]' '{print $NF}'

# Extension (all) : '1.0.1.tar.gz'
    echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'

# Extension (last-2): 'tar.gz'
    echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'

# Basename : 'file'
    echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'

# Basename-extended : 'file.1.0.1.tar'
    echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'

# Path : '/path/to/complex/'
    echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
    # or 
    echo "$f" | grep -Eo '.*[/]'

# Folder (containing the file) : 'complex'
    echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'

# Version : '1.0.1'
    # Defined as 'number.number' or 'number.number.number'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'

    # Version - major : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1

    # Version - minor : '0'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2

    # Version - patch : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3

# All Components : "path to complex file 1 0 1 tar gz"
    echo "$f" | awk -F'[/.]' '{$1=""; print $0}'

# Is absolute : True (exit-code : 0)
    # Return true if it is an absolute path (starting with '/' or '~/'
    echo "$f" | grep -q '^[/]\|^~/'

すべてのユースケースは、中間結果に依存せずに、元のフルパスを入力として使用しています。

20
henfiber

最小で最も単純な解決策(単一行)は、次のとおりです。

$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
16
Ron

受け入れられた答え典型的なケース でうまく働きますが、エッジケース で失敗します:

  • 拡張子のないファイル名(この答えの残りの部分ではサフィックスと呼ばれます)の場合、extension=${filename##*.}は空の文字列ではなく入力ファイル名を返します。
  • 規約に反して、extension=${filename##*.}は最初の.を含みません。
    • .を盲目的に追加することは接尾辞なしのファイル名に対してはうまくいきません。
  • 入力ファイル名がfilename="${filename%.*}"で始まり、それ以上の.文字が含まれていない場合(例:.)、.bash_profileは空ストリングになります - 慣例に反します。

---------

したがって、すべてのEdgeのケースをカバーする 堅牢なソリューションの複雑さ function を必要とします。 it パスのall個の要素を返すことができます

呼び出し例

splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'

入力パスの後の引数は、位置変数namesという自由に選択できます。
興味のない、それより前にある変数をスキップするには、_(使い捨て変数$_を使用する)または''を指定します。たとえば、ファイル名のルートと拡張子のみを抽出するには、splitPath '/etc/bash.bashrc' _ _ fnameroot extensionを使用します。


# SYNOPSIS
#   splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]] 
# DESCRIPTION
#   Splits the specified input path into its components and returns them by assigning
#   them to variables with the specified *names*.
#   Specify '' or throw-away variable _ to skip earlier variables, if necessary.
#   The filename suffix, if any, always starts with '.' - only the *last*
#   '.'-prefixed token is reported as the suffix.
#   As with `dirname`, varDirname will report '.' (current dir) for input paths
#   that are mere filenames, and '/' for the root dir.
#   As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
#   A '.' as the very first char. of a filename is NOT considered the beginning
#   of a filename suffix.
# EXAMPLE
#   splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
#   echo "$parentpath" # -> '/home/jdoe'
#   echo "$fname" # -> 'readme.txt'
#   echo "$fnameroot" # -> 'readme'
#   echo "$suffix" # -> '.txt'
#   ---
#   splitPath '/home/jdoe/readme.txt' _ _ fnameroot
#   echo "$fnameroot" # -> 'readme'  
splitPath() {
  local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
    # simple argument validation
  (( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
    # extract dirname (parent path) and basename (filename)
  _sp_dirname=$(dirname "$1")
  _sp_basename=$(basename "$1")
    # determine suffix, if any
  _sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
    # determine basename root (filemane w/o suffix)
  if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
      _sp_basename_root=$_sp_basename
      _sp_suffix=''
  else # strip suffix from filename
    _sp_basename_root=${_sp_basename%$_sp_suffix}
  fi
  # assign to output vars.
  [[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
  [[ -n $3 ]] && printf -v "$3" "$_sp_basename"
  [[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
  [[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
  return 0
}

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

機能を実行するテストコード:

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

期待される出力 - Edgeのケースに注意してください。

  • 接尾辞のないファイル名
  • .で始まるファイル名(notは接尾辞の始まりとみなされます)
  • /で終わる入力パス(末尾の/は無視されます)
  • ファイル名のみの入力パス(.が親パスとして返されます)
  • .を前に付けたトークンを超えるファイル名(最後のものだけが接尾辞と見なされます):
----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
14
mklement0

ファイルの名前だけが必要な場合は、これを試すことができます。

FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf

# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}

# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}

# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}

echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"

そしてそれがすべて= Dです。

12

あなたは、フィールド番号に-を加えて、すべてのフィールドとそれに続くフィールドを強制的に表示させることができます。

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

したがって、FILEがeth0.pcap.gzの場合、EXTENSIONはpcap.gzになります。

同じロジックを使用して、次のように「 - 」とcutを使用してファイル名を取得することもできます。

NAME=`basename "$FILE" | cut -d'.' -f-1`

これは拡張子を持たないファイル名に対しても機能します。

10
maciek gajewski

それで私が正しく理解したならば、ここでの問題は複数の拡張子を持つファイルの名前と完全な拡張子、例えばstuff.tar.gzを取得する方法です。

これは私のために働く:

fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}

これにより、ファイル名としてstuff、拡張子として.tar.gzが与えられます。これは、0を含む、いくつものエクステンションで動作します。これが同じ問題を抱えている人に役立つことを願っています=)

7
Al3xXx

マジックファイル認識

このStack Overflowの質問に対する多くの良い答えに加えて、私は付け加えたいと思います。

Linuxや他のunixenでは、fileという名前の magic コマンドがあり、これはファイルの最初のいくつかのバイトを分析することによってファイルタイプを検出します。これは非常に古いツールで、最初はプリントサーバー用に使われていました(作成されていないのであれば…私はそれについてはよくわかりません)。

file myfile.txt
myfile.txt: UTF-8 Unicode text

file -b --mime-type myfile.txt
text/plain

標準の拡張は/etc/mime.typesにあります(私の Debian GNU/Linuxデスクトップ。man fileman mime.typesを見てください。おそらくfileユーティリティとmime-supportパッケージをインストールする必要があります):

grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain      asc txt text pot brf srt

正しい拡張子を決定するために bash 関数を作成することができます。少し(完璧ではない)サンプルがあります。

file2ext() {
    local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
    case ${_mimetype##*[/.-]} in
        gzip | bzip2 | xz | z )
            _mimetype=${_mimetype##*[/.-]}
            _mimetype=${_mimetype//ip}
            _basemimetype=$(file -zLb --mime-type "$1")
            ;;
        stream )
            _mimetype=($(file -Lb "$1"))
            [ "${_mimetype[1]}" = "compressed" ] &&
                _basemimetype=$(file -b --mime-type - < <(
                        ${_mimetype,,} -d <"$1")) ||
                _basemimetype=${_mimetype,,}
            _mimetype=${_mimetype,,}
            ;;
        executable )  _mimetype='' _basemimetype='' ;;
        dosexec )     _mimetype='' _basemimetype='exe' ;;
        shellscript ) _mimetype='' _basemimetype='sh' ;;
        * )
            _basemimetype=$_mimetype
            _mimetype=''
            ;;
    esac
    while read -a _line ;do
        if [ "$_line" == "$_basemimetype" ] ;then
            [ "$_line[1]" ] &&
                _basemimetype=${_line[1]} ||
                _basemimetype=${_basemimetype##*[/.-]}
            break
        fi
        done </etc/mime.types
    case ${_basemimetype##*[/.-]} in
        executable ) _basemimetype='' ;;
        shellscript ) _basemimetype='sh' ;;
        dosexec ) _basemimetype='exe' ;;
        * ) ;;
    esac
    [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
      printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
      printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}

この関数は後で使用できるBash変数を設定することができます。

(これは@Peteshの正しい答えからヒントを得たものです):

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

echo "$fullfile -> $filename . $extension"
7
F. Hauri

私は次のスクリプトを使います

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
6
Joydip Datta
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

これはファイル名中の複数のドットとスペースを考慮しますが、拡張子がない場合はファイル名そのものを返します。でも簡単に確認できます。ファイル名と拡張子が同じであることをテストするだけです。

当然、この方法は.tar.gzファイルには機能しません。しかし、それは2段階のプロセスで処理できます。拡張子がgzの場合は、tar拡張子もあるかどうか再度確認してください。

5
Miriam English

fish にファイル名と拡張子を抽出する方法:

function split-filename-extension --description "Prints the filename and extension"
  for file in $argv
    if test -f $file
      set --local extension (echo $file | awk -F. '{print $NF}')
      set --local filename (basename $file .$extension)
      echo "$filename $extension"
    else
      echo "$file is not a valid file"
    end
  end
end

警告:最後のドットで分割します。ドットが含まれているファイル名には適していますが、ドットが含まれている拡張子には適していません。下記の例をご覧ください。

使用法:

$ split-filename-extension foo-0.4.2.Zip bar.tar.gz
foo-0.4.2 Zip  # Looks good!
bar.tar gz  # Careful, you probably want .tar.gz as the extension.

これを行うにはおそらくもっと良い方法があります。それを改善するために私の答えを編集してください。


扱う拡張機能の数が限られていて、それらすべてを知っている場合は、次のことを試してください。

switch $file
  case *.tar
    echo (basename $file .tar) tar
  case *.tar.bz2
    echo (basename $file .tar.bz2) tar.bz2
  case *.tar.gz
    echo (basename $file .tar.gz) tar.gz
  # and so on
end

これはnotに最初の例として警告がありますが、あなたが期待できるエクステンションの数によってはもっと面倒になるかもしれないのであなたはすべてのケースを扱う必要があります。

4
Dennis

これは _ awk _ のコードです。もっと簡単にできます。しかし、私はAWKが苦手です。

filename$ ls
abc.a.txt  a.b.c.txt  pp-kk.txt
filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
txt
txt
txt
4
smilyface

Petesh answerからビルドすると、ファイル名だけが必要な場合は、パスと拡張子の両方を1行で削除できます。

filename=$(basename ${fullname%.*})
3
cvr

単に${parameter%Word}を使う

あなたの場合:

${FILE%.*}

それをテストしたいのなら、以下のすべての作業を行い、ただ拡張子を削除してください。

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
3
enyo

@ mklement0の優れた、そしてぎっしり詰まった、ランダムで便利なbashisms - その他の質問へのその他の回答/ "インターネットを気にすること" ...を全部まとめて、私の(またはあなたの).bash_profileのためのもう少し分かりやすく再利用可能なfunctionは、(私が考える)dirnamebasename/_/whatの頑健なバージョンであるべきです ..

function path { SAVEIFS=$IFS; IFS=""   # stash IFS for safe-keeping, etc.
    [[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return    # demand 2 arguments
    [[ $1 =~ ^(.*/)?(.+)?$ ]] && {     # regex parse the path
        dir=${BASH_REMATCH[1]}
        file=${BASH_REMATCH[2]}
        ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
        # Edge cases for extesionless files and files like ".nesh_profile.coffee"
        [[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
        case "$2" in
             dir) echo      "${dir%/*}"; ;;
            name) echo      "${fnr%.*}"; ;;
        fullname) echo "${fnr%.*}.$ext"; ;;
             ext) echo           "$ext"; ;;
        esac
    }
    IFS=$SAVEIFS
}     

使用例.

SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir        # /path/to.some
path $SOMEPATH name       # .random file
path $SOMEPATH ext        # gzip
path $SOMEPATH fullname   # .random file.gzip                     
path gobbledygook         # usage: -bash <path> <dir|name|fullname|ext>
2
Alex Gray

empty extensionsを許可したい場合は、これが私が思い付くことができる最短のものです:

echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME

1行目の説明:PATH.EXTまたはANYTHINGと一致し、EXTに置き換えられます。 ANYTHINGが一致した場合、extグループはキャプチャされません。

2
Blauhirn

簡単な答え:

POSIX変数 answer を拡張するには、もっと興味深いパターンを実行できます。そのため、ここで詳しく説明する場合は、単純にこれを実行できます。

tar -zxvf $1
cd ${1%.tar.*}

それは.tar .<something>の最後の出現を遮断します。

もっと一般的には、最後に出現した.<something>.<something-else>を削除したい場合

${1.*.*}

うまくいくはずです。

上記の答えのリンクは死んでいるようです。 これは、TLDP から、Bashで直接実行できる一連の文字列操作の優れた説明です。

2
RandyP

上記の答えから、Pythonのを真似るための最短の手綱

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

あなたのファイルが本当に拡張子を持っていると仮定すると、

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
1
commonpike

私見最善の解決策はすでに与えられており(シェルパラメータ展開を使用して)、現時点では最高評価のものです。

しかし、これはdumbsコマンドを使用するだけのものを追加しています。

FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
EXTENSION=$(echo $FILE | tr . '\n' | tail -1)

追加しました ただの楽しみのために :-)

1
Bruno BEAUFILS

tarにこれを行うためのオプションがあるかもしれません。男をチェックしましたか?それ以外の場合は、 Bash文字列展開 :を使用できます。

test="mpc-1.0.1.tar.gz"
noExt="${test/.tar.gz/}" # Remove the string '.tar.gz'
echo $noExt
0
Édouard Lopez

ディレクトリをより便利にするために(パスのないローカルファイルが入力として指定されている場合)、次のようにしました。

# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
    dir="./"
fi

これにより、入力ファイルのベース名に接尾辞を追加するなどの便利なことができます。

outfile=${dir}${base}_suffix.${ext}

testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"

testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
0
Bill Gale

これは私のために働いた唯一のものです:

path='folder/other_folder/file.js'

base=${path##*/}
echo ${base%.*}

>> file

これは文字列の補間にも使用できますが、残念ながら事前にbaseを設定する必要があります。

0
Ken Mueller

これは、大文字と小文字が区別されない場合に名前を一意にするためにBashスクリプトを作成したときに、ファイルの名前と拡張子を見つけるために使用したアルゴリズムです。

#! /bin/bash 

#
# Finds 
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
# 

declare -a fileNames=(
  '.Montreal' 
  '.Rome.txt' 
  'Loundon.txt' 
  'Paris' 
  'San Diego.txt'
  'San Francisco' 
  )

echo "Script ${0} finding name and extension pairs."
echo 

for theFileName in "${fileNames[@]}"
do
     echo "theFileName=${theFileName}"  

     # Get the proposed name by chopping off the extension
     name="${theFileName%.*}"

     # get extension.  Set to null when there isn't an extension
     # Thanks to mklement0 in a comment above.
     extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')

     # a hidden file without extenson?
     if [ "${theFileName}" = "${extension}" ] ; then
         # hidden file without extension.  Fixup.
         name=${theFileName}
         extension=""
     fi

     echo "  name=${name}"
     echo "  extension=${extension}"
done 

テスト実行.

$ config/Name\&Extension.bash 
Script config/Name&Extension.bash finding name and extension pairs.

theFileName=.Montreal
  name=.Montreal
  extension=
theFileName=.Rome.txt
  name=.Rome
  extension=.txt
theFileName=Loundon.txt
  name=Loundon
  extension=.txt
theFileName=Paris
  name=Paris
  extension=
theFileName=San Diego.txt
  name=San Diego
  extension=.txt
theFileName=San Francisco
  name=San Francisco
  extension=
$ 

FYI:完全な音訳プログラムとその他のテストケースはここで見つけることができます: https://www.dropbox.com/jp/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

0
historystamp

あなたが使用することができます

sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-

ファイル名を取得する

sed 's/^/./' | rev | cut -d. -f1  | rev

拡張子を取得する。

テストケース:

echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename"        | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename"        | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1  | rev
0

サンプルファイル/Users/Jonathan/Scripts/bash/MyScript.shを使用して、このコード:

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

${ME}MyScript${MY_EXT}.shになります。


スクリプト:

#!/bin/bash
set -e

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

echo "${ME} - ${MY_EXT}"

いくつかのテスト:

$ ./MyScript.sh 
MyScript - .sh

$ bash MyScript.sh
MyScript - .sh

$ /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh

$ bash /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
0
chown

以下は、さまざまな形式でパスコンポーネントを抽出し、ほとんどのEdgeのケースを処理できるsedソリューションです。

## Enter the input path and field separator character, for example:
## (separatorChar must not be present in inputPath)

inputPath="/path/to/Foo.bar"
separatorChar=":"

## sed extracts the path components and assigns them to output variables

oldIFS="$IFS"
IFS="$separatorChar"
read dirPathWithSlash dirPath fileNameWithExt fileName fileExtWithDot fileExt <<<"$(sed -En '
s/^[[:space:]]+//
s/[[:space:]]+$//
t l1
:l1
s/^([^/]|$)//
t
s/[/]+$//
t l2
:l2
s/^$/filesystem\/\
filesystem/p
t
h
s/^(.*)([/])([^/]+)$/\1\2\
\1\
\3/p
g
t l3
:l3
s/^.*[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\
\2\3\
\3/p
t
s/^.*[/](.+)$/\1/p
' <<<"$inputPath" | tr "\n" "$separatorChar")"
IFS="$oldIFS"

## Results (all use separatorChar=":")

## inputPath        = /path/to/Foo.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.bar
## fileName         = Foo
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/Foobar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foobar
## fileName         = Foobar
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/...bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...bar
## fileName         = ..
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/..bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ..bar
## fileName         = .
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = .bar
## fileName         = .bar
## fileExtWithDot   = 
## fileExt          = 

## inputPath        = /path/to/...
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...
## fileName         = ...
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/Foo.
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.
## fileName         = Foo.
## fileExtWithDot   =
## fileExt          =

## inputPath        = / (the root directory)
## dirPathWithSlash = filesystem/
## dirPath          = filesystem
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        =  (invalid because empty)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        = Foo/bar (invalid because doesn't start with a forward slash)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

仕組みは次のとおりです。

sed入力パスを解析し、次のパスコンポーネントを別々の行に順番に出力します。

  • 末尾のスラッシュ文字を含むディレクトリパス
  • 末尾のスラッシュ文字のないディレクトリパス
  • 拡張子付きのファイル名
  • 拡張子なしのファイル名
  • 先頭にドット文字が付いたファイル拡張子
  • 先行ドット文字なしのファイル拡張子

trは、sedの出力を上記のパスコンポーネントの区切り文字で区切られた文字列に変換します。

readは、フィールド区切り文字(IFS="$separatorChar")として区切り文字を使用し、各パスコンポーネントをそれぞれの変数に割り当てます。

sed構文の仕組みは次のとおりです。

  • s/^[[:space:]]+//およびs/[[:space:]]+$//先頭または末尾の空白文字を削除します
  • t l1および:l1は、次のt関数のためにs関数を更新します
  • s/^([^/]|$)//およびt無効な入力パス(スラッシュで始まらないパス)をテストします。この場合、すべての出力行を空白のままにして、sedコマンドを終了します
  • s/[/]+$//後続のスラッシュを取り除きます
  • t l2および:l2は、次のt関数のためにs関数を更新します
  • s/^$/filesystem\/\\[newline]filesystem/pおよびtは、入力パスがルートディレクトリで構成される特殊なケースをテストします/、その場合はfilesystem /およびfilesystemdirPathWithSlashおよびdirPath出力行に対して、他のすべての出力行を空白のままにして、sedコマンド
  • h入力パスをホールドスペースに保存します
  • s/^(.*)([/])([^/]+)$/\1\2\\[newline]\1\\[newline]\3/pdirPathWithSlashdirPath、およびfileNameWithExt出力行を出力します
  • gホールドスペースから入力パスを取得します
  • t l3および:l3は、次のt関数のためにs関数を更新します
  • s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\\[newline]\3/pおよびtは、fileNamefileExtWithDot、およびfileExtの出力行を出力しますファイル拡張子が存在する場合(英数字のみで構成されると想定)、sedコマンドを終了します
  • s/^.*\[/](.+)$/\1/pfileNameを出力しますが、fileExtWithDotは出力せず、ファイル拡張子が出力しない場合のfileExt出力行を出力します存在する場合、sedコマンドを終了します。
0
scolfax