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?
.tar.bz2
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 esetopt 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 emfile-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 exemplotar
, se não for um tipo de arquivo como0
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