Capturando a extensão em um nome de arquivo

Como obtenho a extensão de arquivo do bash? Aqui “é o que eu tentei:

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

Fazendo isso, posso obter a extensão de bz2 a partir do caminho /dir/subdir/file.bz2, mas tenho um problema com o caminho /dir/subdir/file-1.0.tar.bz2.

Eu preferiria uma solução usando apenas bash sem externo programas se for possível.

Para deixar minha pergunta clara, eu estava criando um script bash para extrair qualquer arquivo fornecido apenas por um único comando de extract path_to_file. para extrair o arquivo é determinado pelo script ao ver seu tipo de compactação ou arquivamento, que pode ser .tar.gz, .gz, .bz2 etc. Acho que isso deve envolver a manipulação de string, por exemplo, se obtiver a extensão .gz então devo verificar se tem a string .tar antes de .gz – se sim, a extensão deve seja .tar.gz.

Comentários

  • file = ” /dir/subdir/file-1.0.tar.bz2

; echo $ {file ## *.} imprime ‘ .bz2 ‘ aqui. Qual é o resultado que você ‘ está esperando?

  • eu preciso .tar.bz2
  • Relacionado : Extraia o nome do arquivo e a extensão no Bash .
  • Resposta

    Se o nome do arquivo for file-1.0.tar.bz2, a extensão é bz2. O método que você está usando para extrair a extensão (fileext=${filename##*.}) é perfeitamente válido¹.

    Como você decide que deseja que a extensão seja tar.bz2 e não bz2 ou 0.tar.bz2? Você precisa responder a esta pergunta primeiro. Então você pode descobrir o que O comando shell corresponde à sua especificação.

    • Uma especificação possível é que as extensões devem começar com uma letra. Essa heurística falha para algumas extensões comuns como 7z, que pode ser mais bem tratado como um caso especial. Aqui “sa bash / ksh / zsh implementação:

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

      Para portabilidade POSIX, você precisa usar uma instrução case para correspondência de padrões.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • Outra especificação possível é que alguns extensões denotam codificações e indicam que é necessária mais remoção. Aqui, “implementação de sa bash / ksh / zsh (exigindo shopt -s extglob em bash e setopt ksh_glob em zsh):

      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%.} 

      Observe que isso considera 0 uma extensão em file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} e as construções relacionadas estão em POSIX , portanto eles funcionam em qualquer shell de estilo Bourne não antigo, como ash, bash, ksh ou zsh.

    Comentários

    • que deveriam ser resolvido, verificando se a string antes do último . token é o tipo de arquivo, por exemplo tar, se não for um tipo de arquivo como 0 a iteração deve terminar.
    • @uray: isso funciona neste caso específico, mas ‘ não é uma solução geral . Considere Maciej ‘ o exemplo de .patch.lzma . Um heur melhor istic seria considerar a string após o último .: se ‘ um sufixo de compressão (.7z, .bz2, .gz, …), continue removendo.
    • @NoamM O que havia de errado com a indentação? Ele ‘ está definitivamente quebrado após sua edição: o código duplamente aninhado é indentado da mesma forma que aninhado individualmente.

    Resposta

    Você pode simplificar as coisas apenas fazendo a correspondência de padrões no nome do arquivo em vez de extrair a extensão duas vezes:

    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 

    Comentários

    • Esta solução é incrivelmente simples.

    Resposta

    $ echo "thisfile.txt"|awk -F . "{print $NF}" 

    Comentários sobre isso aqui: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Comentários

    • não funcionam para .tar.gz extensão
    • Bem, um .tar .gz é na verdade um tar dentro de um arquivo gzip, portanto, funciona no sentido de que remove uma extensão gz de um arquivo gzip.

    Resposta

    Aqui está a minha chance: traduzir pontos em novas linhas, passar por tail, obtenha a última linha:

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

    Resposta

    Um dia eu criei essas funções complicadas:

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

    Achei essa abordagem simples, muito útil em muitos casos, não apenas quando vai sobre extensões.

    Para verificar as extensões – É simples e confiável

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

    Para extensão de corte:

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

    Para alterar extensão:

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

    Ou, se você gosta de “funções úteis:

    ~$ 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 

    PS Se você gostou dessas funções ou achou que eram totalmente usadas, por favor, consulte esta postagem 🙂 (e espero que coloque um comentário).

    Resposta

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

    Por exemplo:

    Comentários

    • Não funciona para todos os casos. Tente com ‘ foo.7z ‘
    • Você precisa de aspas e é melhor usar printf caso o nome do arquivo contenha uma barra invertida ou comece com -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : certo, e eu ‘ eu implementei a mesma especificação do Maciej como exemplo. Qual heurística você sugere que ‘ s melhor do que “começa com uma letra”?
    • @Gilles: acho que há ‘ não é uma solução, a menos que você use uma lista pré-calculada de extensões conhecidas, porque uma extensão pode ser qualquer coisa.

    Resposta

    a resposta baseada no caso de jackman é muito boa e portátil, mas se você quiser apenas o nome do arquivo e a extensão em uma variável, encontrei esta solução:

    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 

    Ele só funciona com extensões duplas e a primeira deve ser “tar”.

    Mas você pode alterar a linha de teste “tar” com um teste de comprimento de string e repetir a correção várias vezes .

    Resposta

    Eu resolvi usando isto:

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

    mas isso só funciona para tipo de arquivamento conhecido, neste caso apenas tar

    Deixe uma resposta

    O seu endereço de email não será publicado. Campos obrigatórios marcados com *