Ersetzen Sie Punkte durch Unterstriche in Dateinamen, wobei die Erweiterung intakt bleibt

8

Ich habe ein Bash-Skript, mit dem ich versuche, Punkte in Dateinamen zu ersetzen und durch Unterstriche zu ersetzen, wobei die Erweiterung intakt bleibt (ich bin übrigens auf Centos 6). Wie Sie der folgenden Ausgabe entnehmen können, funktioniert das Skript, wenn ein Punkt ersetzt werden muss. In Fällen, in denen der einzige Punkt die Erweiterung ist, versucht das Skript dennoch, die Datei umzubenennen, anstatt sie zu ignorieren. Kann jemand darauf hinweisen, wie ich besser damit umgehen soll? Vielen Dank für jede Hilfe.

Mein (fehlerhaftes) Skript:

#!/bin/bash

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

Beispieleingabe:

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

Ausgabe:

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
quelle
1
Was ist mit kniffligen Fällen wie tar.gzDateien? Sie möchten, dass sie sich auflösen file.tar.gz, nicht file_tar.gz.
IQAndreas

Antworten:

10

Ich glaube, dass dieses Programm tun wird, was Sie wollen. Ich habe es getestet und es funktioniert in mehreren interessanten Fällen (z. B. überhaupt keine Erweiterung):

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

Das Hauptproblem, das Sie hatten, war, dass die ##Erweiterung nicht das tat, was Sie wollten. Ich habe die Erweiterung der Shell-Parameter in Bash immer als eine Art schwarze Kunst angesehen. Die Erklärungen im Handbuch sind nicht ganz klar und es fehlen unterstützende Beispiele dafür, wie die Erweiterung funktionieren soll. Sie sind auch ziemlich kryptisch.

Persönlich hätte ich ein kleines Skript geschrieben sed, das den Namen so fummelte, wie ich es wollte, oder ein kleines Skript geschrieben perl, das einfach das Ganze tat. Eine der anderen Personen, die geantwortet haben, hat diesen Ansatz gewählt.

Eine andere Sache, auf die ich hinweisen möchte, ist meine Verwendung von Zitaten. Jedes Mal, wenn ich etwas mit Shell-Skripten mache, erinnere ich die Leute daran, sehr vorsichtig mit ihren Zitaten umzugehen. Eine große Ursache für Probleme in Shell-Skripten ist die Shell, die Dinge interpretiert, die sie nicht soll. Und die Angebotsregeln sind alles andere als offensichtlich. Ich glaube, dieses Shell-Skript ist frei von Zitierproblemen.

Allgegenwärtig
quelle
Das macht den Job in der Tat gut :) Danke auch für die Erklärung zur Shell-Erweiterung.
bsod99
Wie kann ich neben dem Ersetzen von Punkt (.) Durch (_) Leerzeichen in den Dateinamen löschen?
Discipulus
Dies ist ein sehr schönes Skript. Die einzigen Verbesserungen, die ich sehen kann, sind, dass ein Verzeichnisbaum wieder verwendet wird und Sie $ 1 durch $ 2 ersetzen können, aber diese sind geringfügig. (Oder, wie mein Lehrer sagte, "als Übung für den Schüler verlassen"!)
lbutlr
4

Verwenden Sie for thisfile in *.*.*(dh durchlaufen Sie Dateien mit zwei oder mehr Punkten im Namen). Denken Sie daran, Ihre Variablen in Anführungszeichen --zu setzen und das Ende der Optionen wie in zu markierenmv -- "$thisfile" "$newname.$extension"

Mit zsh.

autoload -U zmv
zmv '(*).(*)' '${1//./_}.$2'
Stéphane Chazelas
quelle
Ich habe Ihren Vorschlag möglicherweise falsch umgesetzt, aber dies führt zu mv - . . * _ . *
bsod99
3

Wie wäre es damit:

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

HAFTUNGSAUSSCHLUSS: Versuchen Sie es zuerst in einem Dummy-Verzeichnis, ich habe es nicht getestet!

Joseph R.
quelle
Knapp, kernig, fast nur zum Schreiben! Yay Perl! kichern
Omnifarious
Nur schreiben? Ich wette, das ist das erste Mal, dass Perl so genannt wird ! ( wieder kichern )
Joseph R.
Hat versucht, die Lesbarkeit dort zu verbessern. Hoffe, das fühlt sich weniger "nur schreiben" an :)
Joseph R.
1
Ja, das hilft einem Haufen. Es ist schade, dass es den einzigen vernünftigen Charakter gibt, den man als Platzhalter verwenden kann /.
Omnifarious
2

Es sieht so aus, als ob einige gute Antworten bereits verfügbar sind, aber hier ist eine andere Verwendung von trund sed:

#!/bin/bash

for file in *; do
    newname=$(echo $file | tr '.' '_' | sed 's/\(.*\)_\([^_]*\)$/\1.\2/g')
    [ "$newname" != "$file" ] && mv "$file" "$newname"
done
JC Yamokoski
quelle
Wie sedentscheidet man, auf was .*man maximal kaut? Ich würde viel besser fühlt darüber , wenn der zweite .*war [^_]*.
Omnifarious
Ich glaube auch, dass dies auf den Fall 'keine Erweiterung' und einen Dateinamen mit \tZeichen darin zurückzuführen ist.
Omnifarious
Dies war definitiv nur eine schnelle Lösung. Vielen Dank für Ihre Eingabe ... Ich habe den regulären Ausdruck korrigiert, um Ihrer ersten Empfehlung gerecht zu werden. Ich werde sehen, ob ich es optimieren kann, um die anderen Umstände zu berücksichtigen, die Sie erwähnt haben.
JC Yamokoski
echo -n "$file"würde funktionieren. :-) Oh, und es geht "um den $( ... )Ausdruck (aka "$( ... )") würde es tun.
Omnifarious
1

In dieser Version können Sie explizit die Anzahl der Punkte auswählen, die Sie behalten möchten, beginnend auf der rechten Seite.

Außerdem werden zusätzlich zu den Punkten andere Zeichen ersetzt und / oder gelöscht, und das Ersatzzeichen wird -anstelle eines Unterstrichs verwendet. Dies kann jedoch leicht geändert werden.

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