Remplacez les points par des traits de soulignement dans les noms de fichiers, en laissant l'extension intacte

8

J'ai un script bash que j'essaie d'obtenir pour remplacer les points dans les noms de fichiers et les remplacer par des traits de soulignement, en laissant l'extension intacte (je suis sur Centos 6 btw). Comme vous pouvez le voir sur la sortie ci-dessous, le script fonctionne lorsqu'il y a un point à remplacer, mais dans les cas où le seul point est l'extension, le script essaie toujours de renommer le fichier, au lieu de l'ignorer. Quelqu'un peut-il indiquer comment je devrais mieux gérer cela? Merci pour toute aide.

Mon script (défectueux):

#!/bin/bash

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

Exemple d'entrée:

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

Production:

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
bsod99
la source
1
Qu'en est-il des cas délicats tels que les tar.gzfichiers? Vous voudriez qu'ils se résolvent file.tar.gz, non file_tar.gz.
IQAndreas

Réponses:

10

Je crois que ce programme fera ce que vous voulez. Je l'ai testé et cela fonctionne sur plusieurs cas intéressants (comme aucune extension du tout):

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

Le principal problème que vous aviez était que l' ##expansion ne faisait pas ce que vous vouliez. J'ai toujours considéré l'expansion des paramètres du shell dans bash comme un art noir. Les explications contenues dans le manuel ne sont pas complètement claires et il leur manque des exemples à l'appui du fonctionnement de l'extension. Ils sont également assez cryptiques.

Personnellement, j'aurais écrit un petit script dans sedlequel je bidouillerais le nom comme je le voulais, ou j'aurais écrit un petit script dans perllequel je ferais tout. L'une des autres personnes qui ont répondu a adopté cette approche.

Une autre chose que je voudrais souligner est mon utilisation de la citation. Chaque fois que je fais quoi que ce soit avec des scripts shell, je rappelle aux gens de faire très attention à leurs citations. Une énorme source de problèmes dans les scripts shell est le shell qui interprète des choses qu'il n'est pas censé faire. Et les règles de cotation sont loin d'être évidentes. Je crois que ce script shell est exempt de problèmes de citation.

Très varié
la source
Cela fait en effet le travail bien :) Merci pour l'explication aussi concernant l'expansion du shell.
bsod99
En plus de remplacer le point (.) Par (_), comment puis-je supprimer des espaces dans les noms de fichiers.
discipulus
Ceci est un très joli script. Les seules améliorations que je peux voir sont de le faire récurrer une arborescence de répertoires et de vous permettre de remplacer $ 1 par $ 2, mais ceux-ci sont mineurs. (Ou, comme disait mon professeur, "laissé comme exercice pour l'élève"!)
lbutlr
4

Utiliser for thisfile in *.*.*(c'est-à-dire boucler sur des fichiers avec deux points ou plus dans leur nom). N'oubliez pas de citer vos variables et utilisez --pour marquer la fin des options comme dansmv -- "$thisfile" "$newname.$extension"

Avec zsh.

autoload -U zmv
zmv '(*).(*)' '${1//./_}.$2'
Stéphane Chazelas
la source
J'ai peut-être mal appliqué votre suggestion, mais cela se traduit par mv - . . * _ . *
bsod99
3

Que dis-tu de ça:

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

AVERTISSEMENT: essayez-le d'abord sur un répertoire factice, je ne l'ai pas testé!

Joseph R.
la source
Laconique, concis, presque en écriture seule! Ouais perl! gloussement
Omnifarious
Écriture seule? Je parie que c'est la première fois que Perl s'appelle ainsi! ( rire de nouveau )
Joseph R.
A tenté d'améliorer la lisibilité là-bas. J'espère que cela semble moins "en écriture seule" :)
Joseph R.
1
Oui, ça aide beaucoup. C'est dommage que le seul caractère raisonnable à utiliser comme espace réservé existe /.
Omnifarious
2

Il semble que de bonnes réponses soient déjà disponibles, mais voici une autre utilisation tret sed:

#!/bin/bash

for file in *; do
    newname=$(echo $file | tr '.' '_' | sed 's/\(.*\)_\([^_]*\)$/\1.\2/g')
    [ "$newname" != "$file" ] && mv "$file" "$newname"
done
JC Yamokoski
la source
Comment seddécide-t-on auquel .*appliquer le munch maximal? Je me sens beaucoup mieux à ce sujet si le second .*était [^_]*.
Omnifarious
Je crois également que cela tombe sur le cas de «pas d'extension» et un nom de fichier avec des \tcaractères.
Omnifarious
Ce n'était définitivement qu'une solution rapide. Merci pour votre contribution ... J'ai corrigé l'expression régulière pour tenir compte de votre première recommandation. Je vais voir si je peux le modifier pour tenir compte des autres circonstances que vous avez mentionnées.
JC Yamokoski
echo -n "$file"travaillerait. :-) Oh, et c'est "autour de l' $( ... )expression (aka "$( ... )") le ferait.
Omnifarious
1

Cette version vous permet de sélectionner explicitement le nombre de points que vous souhaitez conserver, en commençant par le côté droit.

En outre, il remplacera et / ou supprimera d'autres caractères en plus des points, et le caractère de remplacement est -au lieu d'un trait de soulignement, mais cela peut être facilement modifié.

#!/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/^-\(.\)/\1/; s/\(.\)-$/\1/'
)

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
Ernest A
la source