Substitua pontos por sublinhados em nomes de arquivos, deixando a extensão intacta

8

Eu tenho um script bash que eu estou tentando conseguir para substituir pontos em nomes de arquivos e substituí-los por sublinhados, deixando a extensão intacta (estou no Centos 6 btw). Como você pode ver na saída abaixo, o script funciona quando há um ponto para substituir, mas nos casos em que o único ponto é a extensão, o script ainda tenta renomear o arquivo, em vez de ignorá-lo. Alguém pode apontar como eu deveria lidar com isso melhor? Obrigado por qualquer ajuda.

Meu script (com defeito):

#!/bin/bash

for THISFILE in *
do
  filename=${THISFILE%\.*}
  extension=${THISFILE##*\.}
  newname=${filename//./_}
  echo "mv $THISFILE ${newname}.${extension}"
  #mv $THISFILE ${newname}.${extension}
done

Exemplo de entrada:

1.3MN-Pin-Eurotunnel-Stw505.51.024-EGS-130x130.jpg
Wear-Plates.jpg

Saída:

mv 1_3MN-Pin-Eurotunnel-Stw505_51_024-EGS1-130x130.jpg 1_3MN-Pin-Eurotunnel-Stw505_51_024-EGS1-130x130.jpg
mv Wear-Plates_jpg.Wear-Plates_jpg Wear-Plates_jpg.Wear-Plates_jpg
    
por bsod99 26.11.2012 / 15:10

5 respostas

10

Eu acredito que este programa fará o que você quiser. Eu testei e funciona em vários casos interessantes (como nenhuma extensão):

#!/bin/bash

for fname in *; do
  name="${fname%\.*}"
  extension="${fname#$name}"
  newname="${name//./_}"
  newfname="$newname""$extension"
  if [ "$fname" != "$newfname" ]; then
    echo mv "$fname" "$newfname"
    #mv "$fname" "$newfname"
  fi
done

O principal problema que você teve foi que a expansão ## não estava fazendo o que você queria. Eu sempre considerei a expansão dos parâmetros shell no bash como uma espécie de arte negra. As explicações no manual não estão completamente claras, e elas não têm nenhum exemplo de como a expansão deve funcionar. Eles também são bastante enigmáticos.

Pessoalmente, eu teria escrito um pequeno script em sed que mexeu no nome da maneira que eu queria, ou escrevi um pequeno script em perl que acabou de fazer a coisa toda. Uma das outras pessoas que responderam adotou essa abordagem.

Uma outra coisa que gostaria de salientar é o meu uso de citações. Toda vez que faço algo com shell scripts, lembro às pessoas que tenham muito cuidado com suas citações. Uma grande fonte de problemas em scripts de shell é o shell interpretando coisas que não deveriam. E as regras de cotação estão longe de ser óbvias. Eu acredito que este script de shell está livre de problemas de cotação.

    
por 26.11.2012 / 17:01
4

Use for thisfile in *.*.* (ou seja, faça um loop sobre arquivos com dois pontos ou mais em seu nome). Lembre-se de citar suas variáveis e use -- para marcar o final das opções como em mv -- "$thisfile" "$newname.$extension"

com zsh.

autoload -U zmv
zmv '(*).(*)' '${1//./_}.$2'
    
por 26.11.2012 / 15:15
3

Que tal isso:

perl -e '
         @files = grep {-f} glob "*";
         @old_files = @files;
         map {
              s!(.*)\.!$1/!;
              s!\.!_!g;
              s!/!.!
             } @files;
         rename $old_files[$_] => $files[$_] for (0..$#files)
        '

AVISO LEGAL: tente em um diretório fictício primeiro, eu não testei!

    
por 26.11.2012 / 16:58
2

Parece que algumas boas respostas já estão disponíveis, mas aqui está outra usando tr e sed :

#!/bin/bash

for file in *; do
    newname=$(echo $file | tr '.' '_' | sed 's/\(.*\)_\([^_]*\)$/./g')
    [ "$newname" != "$file" ] && mv "$file" "$newname"
done
    
por 26.11.2012 / 17:08
1

Esta versão permite que você selecione explicitamente o número de pontos que deseja manter, começando pelo lado direito.

Além disso, ele substituirá e / ou excluirá outros caracteres além dos pontos, e o caractere de substituição será - em vez de um sublinhado, mas isso pode ser facilmente alterado.

#!/bin/sh
# Rename files by replacing Unix-unfriendly characters.

usage () {
    cat <<EOF
usage: $0 [OPTIONS] [--] [FILE [FILE...]]
Rename files by replacing Unix-unfriendly characters.

Options:
 -p N              preserve last N dots in filename, or keep all
                   dots if N < 0 (default: 1)
       --help      show this help and exit
EOF
}

error () {
    printf "%s\n" "$1" 1>&2
}

delete_chars="()[]{}*?!^~%\\<>&\$#|'\'\""
replace_chars=" _.,;-"

unixify_string () (
    printf '%s\n' "$1" \
        | tr -d "$delete_chars" \
        | tr -s "$replace_chars" - \
        | to_lower \
        | sed 's/^-\(.\)//; s/\(.\)-$//'
)

to_lower () {
    sed 's/.*/\L&/'
}

split () (
    # split '.x.x.x.x'  0 -> '/x.x.x.x.x
    # split '.x.x.x.x'  1 -> '/x.x.x.x/x
    # split '.x.x.x.x'  2 -> '/x.x.x/x/x
    # split '.x.x.x.x' -1 -> '/x/x/x/x/x
    nf=$(printf '%s\n' "$1" | tr -d -C . | wc -c)
    if [ $2 -lt 0 ]; then
        keep=0
    else
        keep=$((nf-$2))
    fi
    IFS=. i=0 out= sep=
    for part in $1; do
        out="$out$sep$part"
        if [ -z "$out" -o $i -ge $keep ]; then
            sep=/
        else
            sep=.
        fi
        i=$(($i+1))
    done
    printf '%s\n' "$out"
)

unixify () (
    IFS=/ out= sep=
    for part in $(split "$1" $2); do
        out="$out$sep$(unixify_string "$part")"
        sep=.
    done
    printf '%s\n' "$out"
)

rename_maybe () (
    dir="$(dirname "$1")"
    name="$(basename "$1")"
    newname="$(unixify "$name" $2)"
    if [ "$newname" != "$name" ]; then
        mv -i "$dir/$name" "$dir/$newname"
    fi
)

# command line arguments

short_opts=p:
long_opts=help

args="$(LC_ALL=C getopt -n "$0" -s sh -o $short_opts -l $long_opts -- "$@")"
if [ $? -eq 0 ]; then
    eval set -- "$args"
else
    exit 1
fi

p=
while [ $# -gt 0 ]; do
    case "$1" in
        --help)
            usage; exit 0 ;;
        -p)
            p="$2"; shift
            if ! [ "$p" -eq "$p" ] 2> /dev/null; then
                error "$0: option requires integer argument -- 'p'"
                exit 1
            fi ;;
        --)
            shift; break ;;
        -*)
            error "$0: illegal option -- '$1'"
            exit 1 ;;
        *)
            break
    esac
    shift
done

# defaults
p=${p:-1}

# echo p=$p
# echo "$@"
# echo n=$#
# exit

if [ $# -lt 1 ]; then
    error "$0: required non-option argument missing"
    exit 1
fi

for file in "$@"; do
    rename_maybe "$file" $p
done
    
por 08.01.2014 / 20:32