파일 이름으로 확장자를 잡아


33

bash에서 파일 확장자를 어떻게 얻습니까? 내가 시도한 것은 다음과 같습니다.

filename=`basename $filepath`
fileext=${filename##*.}

그렇게 bz2하면 경로에서 확장을 얻을 수 /dir/subdir/file.bz2있지만 경로 에 문제가 /dir/subdir/file-1.0.tar.bz2있습니다.

가능하다면 외부 프로그램없이 bash 만 사용하는 솔루션을 선호합니다.

내 질문을 분명히하기 위해의 단일 명령으로 주어진 아카이브를 추출하는 bash 스크립트를 작성했습니다 extract path_to_file. 그것의 압축을 보거나 유형을 보관하여 스크립트에 의해 결정되는 방법 파일을 추출하는 방법, 그게 내가 확장을받을 경우 .bz2로 등 I 예를 들어,이 문자열 조작을 포함해야한다 생각에 .gz, .tar.gz를 할 수 .gz그때 .tar앞에 문자열이 있는지 확인 .gz해야합니다 .tar.gz. 확장명은이어야합니다 .


2
file = "/ dir / subdir / file-1.0.tar.bz2"; echo $ {file ## *.}는 여기에 '.bz2'를 인쇄합니다. 당신이 기대하는 결과는 무엇입니까?
axel_c

1
나는 필요하다.tar.bz2
uray

답변:


19

파일 이름이 file-1.0.tar.bz2인 경우 확장자는 bz2입니다. 확장명 ( fileext=${filename##*.}) 을 추출하는 데 사용하는 방법 은 완벽하게 유효합니다 ¹.

당신은 어떻게 당신이 확장 될 것인지로 결정 tar.bz2하지 bz20.tar.bz2? 이 질문에 먼저 답해야합니다. 그런 다음 사양에 맞는 쉘 명령을 알아낼 수 있습니다.

  • 가능한 사양 중 하나는 확장자가 문자로 시작해야한다는 것입니다. 이 휴리스틱은와 같은 몇 가지 일반적인 확장에 실패 7z하며 특수 사례로 가장 잘 처리 될 수 있습니다. bash / ksh / zsh 구현은 다음과 같습니다.

    basename=$filename; fileext=
    while [[ $basename = ?*.* &&
             ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]]
    do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    fileext=${fileext%.}

    POSIX 이식성을 case위해 패턴 일치를위한 명령문 을 사용해야합니다 .

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do 
  • 또 다른 가능한 사양은 일부 확장은 인코딩을 나타내며 추가 스트리핑이 필요하다는 것을 나타냅니다. bash / ksh / zsh 구현은 다음과 같습니다 ( shopt -s extglobbash 및 setopt ksh_globzsh 아래 필요 ).

    basename=$filename
    fileext=
    while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    if [[ $basename = ?*.* ]]; then
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    fi
    fileext=${fileext%.}

    0의 확장명으로 간주 됩니다 file-1.0.gz.

¹ 및 관련 구문은 POSIX 에 있으므로 ash, bash, ksh 또는 zsh와 같은 비 앤티크 (Anti-Anquene) Bourne 스타일 쉘에서 작동합니다. ${VARIABLE##SUFFIX}


마지막 .토큰 앞의 문자열 이 아카이브 유형 인지 여부를 확인하여 해결해야합니다 ( 예 tar: 0반복 과 같은 아카이브 유형이 아닌지 여부) .
uray September

2
@uray :이 특별한 경우에는 작동하지만 일반적인 해결책은 아닙니다. Maciej의 예제를.patch.lzma 고려하십시오 . 더 나은 발견 문자열을 고려하는 것입니다 마지막 .: 그것은 압축 접미사 인 경우 ( .7z, .bz2, .gz, ...), 제거를 계속합니다.
Gilles 'SO- 악마 그만'

@NoamM 들여 쓰기에 어떤 문제가 있었습니까? 편집 후 분명히 깨졌습니다. 이중 중첩 코드는 단일 중첩과 동일하게 들여 쓰기됩니다.
Gilles 'SO- 악마 그만'

22

확장명을 두 번 추출하는 대신 파일 이름에 패턴 일치를 수행하여 문제를 단순화 할 수 있습니다.

case "$filename" in
    *.tar.bz2) bunzip_then_untar ;;
    *.bz2)     bunzip_only ;;
    *.tar.gz)  untar_with -z ;;
    *.tgz)     untar_with -z ;;
    *.gz)      gunzip_only ;;
    *.zip)     unzip ;;
    *.7z)      do something ;;
    *)         do nothing ;;
esac

이 솔루션은 매우 간단합니다.
AsymLabs


2

여기에 내 장면이 있습니다 : 점을 줄 바꿈으로 변환하고을 통과 tail하고 마지막 줄을 얻으십시오.

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678

0
echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}

예를 들면 다음과 같습니다.

% echo $filename
2.6.35-zen2.patch.lzma
% echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}
.patch.lzma

모든 경우에 작동하지는 않습니다. 'foo.7z'로 시도
axel_c

인용 부호가 필요 printf하고 파일 이름에 백 슬래시가 포함되거나 다음으로 시작하는 경우 더 잘 사용 됩니다 -."${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
Gilles 'SO-stop be evil'9

@axel_c : 맞습니다. 예를 들어 Maciej와 동일한 사양을 구현했습니다. 편지로 시작하는 것보다 나은 휴리스틱은 무엇이라고 제안합니까?
Gilles 'SO- 악마 그만'

1
@Gilles : 확장 프로그램이 무엇이든 될 수 있기 때문에 미리 계산 된 알려진 확장 프로그램 목록을 사용하지 않으면 해결책이 없다고 생각합니다.
axel_c

0

어느 날 나는 그 까다로운 기능을 만들었습니다.

# args: string how_many
function get_last_letters(){ echo ${1:${#1}-$2:$2}; }
function cut_last_letters(){ echo ${1:0:${#1}-$2}; }

확장에 관한 것뿐만 아니라 많은 경우에 매우 유용한이 간단한 접근 방법을 찾았습니다.

확장 프로그램 확인- 간단하고 안정적 ​​임

~$ get_last_letters file.bz2 4
.bz2
~$ get_last_letters file.0.tar.bz2 4
.bz2

절단 연장 용 :

~$ cut_last_letters file.0.tar.bz2 4
file.0.tar

확장명 변경 :

~$ echo $(cut_last_letters file.0.tar.bz2 4).gz
file.0.tar.gz

또는 "유용한 기능 :

~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; }
~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz
file.0.tar.gz

추신 : 당신이 그 기능을 좋아하거나 그 기능이 충분히 사용 된 것을 발견했다면,이 게시물을 참조하십시오 :).


0

jackman case-based answer은 꽤 훌륭하고 이식 가능하지만 변수의 파일 이름과 확장자를 원한다면이 솔루션을 찾았습니다.

INPUTFILE="$1"
INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev )
INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[A-Z]' '[a-z]' ) # force lowercase extension
INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d'.' -f2- | rev`"

# fix for files with multiple extensions like "gbamidi-v1.0.tar.gz"
INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d'.' -f1 | rev )
if [ "$INPUTFILEEXT2" = "tar" ]; then
    # concatenate the extension
    INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT"
    # update the filename
    INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d'.' -f2- | rev`"
fi

이중 확장에서만 작동하며 첫 번째 확장은 "tar"여야합니다.

그러나 문자열 길이 테스트로 "tar"테스트 라인을 변경하고 수정을 여러 번 반복 할 수 있습니다.


-1

나는 이것을 사용하여 그것을 해결했다 :

filename=`basename $filepath`
fileext=${filename##*.}
fileext2=${filename%.*}
fileext3=${fileext2##*.}
if [ "$fileext3" == "tar" ]; then
    fileext="tar."$fileext
fi

그러나 이것은 알려진 보관 유형에서만 작동합니다.이 경우에만 tar

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.