Skript zum Entfernen von Leerzeichen und Kleinbuchstaben in Dateinamen

7

Ich versuche, ein Skript zu schreiben, das Leerzeichen durch "-" ersetzt und alle Buchstaben für alle Dateien im aktuellen Verzeichnis in Kleinbuchstaben schreibt.

for x in 'ls'
    do
        if [ ! -f $x ]; then
            continue
        fi

        lc = `echo $x | tr '[A-Z]' '[a-z]'`
        if [ $lc != $x ]; then
            mv $x $lc
        fi
    done

find -name "* *" -type f | rename 's/ /-/g'

Ich erhalte folgende Ausgabe: call: umbenennen von in Dateien ...

Die Namen ändern sich jedoch nicht, Beispiel: 252680610243-Analyzed Sample2 2Jul12.txt

Ich habe die Berechtigungen mit geändert chmod 706. Würde dies das Problem verursachen? Was fehlt mir hier?

Hier ist die Ausgabe von bash -x lower.sh:

+ for x in ''\''ls'\'''
+ '[' '!' -f ls ']'
+ continue
+ find -name '* *' -type f
+ rename 's/ /-/g'
call: rename from to files...
Joe
quelle
1
Warum kommentierst du nicht einfach das mvund wirfst ein echo $lcoder zwei ein, damit du sehen kannst, was du tust, und arbeitest dann von dort aus? Siehe auch bash -x: tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html Das Erlernen des Fischens ist besser als nach Fisch zu fragen;)
Goldlöckchen
Sie können reguläre Ausdrücke für Kleinbuchstaben verwenden, ohne tr zu verwenden. Siehe stackoverflow.com/questions/689495/…
Erik
Ich kenne die Regex-Methode, um dies zu tun. Ich versuche nur, mich ein bisschen mit Bash-Skripten vertraut zu machen. Ein Update finden Sie im OP. Interessant war, dass das Ersetzen mv...durch echo $lcdie Ausgabe des Codes nicht veränderte.
Joe
1
Ich würde denken, du willst `ls`nicht 'ls'- oder noch besser, nur for x in *. Mein Punkt über die Echos und Sachen war, dass Sie die Ausgabe jedes Befehls berücksichtigen sollten (z. B. for x in 'ls'ist überhaupt nicht das, was Sie denken, dass es ist).
Goldlöckchen
Das war ein Fehler, jetzt dasUnexpected arguments passed on cmd line ./lower.sh: line 8: [: !=: unary operator expected
Joe

Antworten:

8

Bei Debian und Derivaten (einschließlich Ubuntu) ist dies mit einem einfachen Aufruf an rename:

$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b

Sie haben renamejedoch ein anderes Dienstprogramm mit demselben Namen .

Dennis Kaarsemaker
quelle
Ich habe den Umbenennungsbefehl komplett vergessen :)
TP
Das hat bei mir nicht funktioniert
Joe
Sie verwenden wahrscheinlich das Umbenennen von util-linux und nicht das mit Perl gelieferte.
Dennis Kaarsemaker
1
Beachten Sie, dass nur ASCII-Buchstaben übersetzt werden, keine anderen Buchstaben im aktuellen Gebietsschema, wenn dieses Gebietsschema nicht auf ASCII basiert.
Stéphane Chazelas
Hmm ... Ich frage mich, ob die Perl-Umbenennung lc als Argument akzeptiert. Perls lc erledigt die Nicht-ASCII-Buchstaben.
Dennis Kaarsemaker
6

Es gibt verschiedene Probleme mit Ihrem Skript.

for x in 'ls'

Sie durchlaufen eine Liste mit einem Wort : ls. Sie `ls`wollten wahrscheinlich schreiben (mit Backticks), um die Ausgabe von zu analysieren ls. Analysieren Sie nicht die Ausgabe vonls : Dies funktioniert nicht, wenn die Dateinamen Sonderzeichen wie Leerzeichen enthalten. Die Shell verfügt bereits über eine integrierte Methode zum Auflisten der Dateien in einem Verzeichnis: Platzhalter. Also schreibe for x in *.

        if [ ! -f $x ]; then

Dies funktioniert nicht, wenn der Dateiname Leerzeichen und andere Sonderzeichen enthält. Wenn Sie $xexterne Anführungszeichen schreiben , wird das Ergebnis in separate Wörter aufgeteilt (und jedes Wort wird zum Booten als Platzhaltermuster interpretiert). Um diesen Effekt zu vermeiden, setzen Sie $xdoppelte Anführungszeichen : if [ ! -f "$x" ]; then. Sie können sich an komplexe Regeln erinnern oder einfach immer doppelte Anführungszeichen für Variablensubstitutionen verwenden.

Einige andere Zeilen sind aufgrund fehlender doppelter Anführungszeichen unterbrochen. Setzen Sie sie einfach um jede variable Substitution.

       lc = `echo $x | tr '[A-Z]' '[a-z]'`

Dies funktioniert aufgrund der Leerzeichen um das Gleichheitszeichen nicht: Diese Zeile führt den lcBefehl aus und übergibt ihn =als erstes Argument (und andere Argumente). Eine Zuweisung in der Shell darf kein Leerzeichen um das =Zeichen haben.

Sie brauchen keine Klammern um die Argumente von tr. Hier funktioniert der Befehl zufällig, weil Sie sagen, dass Sie [nach [und ]nach ]zusätzlich zum Kleinbuchstaben ersetzen sollen .

Ich empfehle die Verwendung von Dollar-Klammern $(…)anstelle von Backticks `…`für die Befehlsersetzung. Sie sind äquivalent, außer dass Backticks komplexe Regeln für das Zitieren von Dingen enthalten, während $(…)sie eine sehr intuitive Syntax haben. Alles in allem sollte dieser Befehl lauten:lc=$(echo "$x" | tr A-Z a-z)

find -name "* *" -type f | rename 's/ /-/g'

Ihre Fehlermeldung zeigt an, dass Sie den renameBefehl aus dem util-linux-Paket haben und nicht das Perl-Skript, das unter Debian und Derivaten (einschließlich Ubuntu) gefunden wurde. Die von Ihnen verwendete Syntax ist nur mit dem Perl-Skript sinnvoll.

Wenn Sie das Perl-Skript verwenden, kann es die Groß- und Kleinschreibung in Kleinbuchstaben ändern, sodass Sie dies nicht vorher tun müssen. Und Sie müssen nicht aufrufen, es findsei denn, Sie möchten Dateien auch in Unterverzeichnissen umbenennen (was in Ihrem ersten Teil nicht behandelt wird). Wenn Sie alle Dateien im aktuellen Verzeichnis umbenennen möchten, können Sie einfach schreiben:

prename 'tr/A-Z /a-z-/' -- *

Wenn Sie nur reguläre Dateien umbenennen möchten (jedoch keine Unterverzeichnisse, symbolischen Links usw.), verwenden Sie find:

find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +

Wenn Sie auch auf Dateien in Unterverzeichnissen reagieren möchten und die Verzeichnisnamen Leerzeichen oder Großbuchstaben enthalten können, müssen Sie darauf achten, diese in Ruhe zu lassen. Da Sie unter Linux arbeiten, können Sie mit einem Argument -execdiraufrufen prename, das keinen Verzeichnisnamen enthält.

find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +

Was ist, wenn Sie das Perl- renameDienstprogramm nicht haben ? Das Dienstprogramm util-linux renamehilft Ihnen hier nicht weiter. Sie können einen weiteren Ersatz in Ihre Schleife einfügen.

for x in ./*; do
  if [ -f "$x" ]; then
    y=$(echo "$x" | tr 'A-Z-' 'a-z ')
    mv "$x" "$y"
  fi
done

Mit bash müssen Sie nicht aufrufen tr, sondern können die String-Substitutionskonstrukte verwenden.

for x in ./*; do
  if [ -f "$x" ]; then
    lc=${x,,}            # convert all letters to lowercase
    y=${lc// /-}         # replace spaces by hyphens
    if [ "$x" != "$y" ]; then
      mv "$x" "$y"
    fi
  fi
done

Wenn Sie auch auf Dateien in Unterverzeichnissen reagieren möchten, können Sie einen rekursiven Glob verwenden (aktiviert durch die globstarOption). Achten Sie auch hier darauf, den Verzeichnisteil in Ruhe zu lassen, wenn er Großbuchstaben oder Leerzeichen enthalten kann.

shopt -s globstar
for x in ./**/*; do
  if [ -f "$x" ]; then
    old_basename=${x##*/}
    new_basename=${old_basename,,}
    new_basename=${new_basename// /-}
    if [ "$new_basename" != "$old_basename" ]; then
      mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
    fi
  fi
done

Geben Sie in zsh autoload -U zmvyour ein .zshrc, und Sie können das zmvmit dem Glob-Qualifikationsmerkmal verwenden . , um nur reguläre Dateien und Parametererweiterungskonstrukte für die Umbenennung abzugleichen:

zmv -Q '*(.)' '${(L)f// /-}'

So bearbeiten Sie auch Dateien in Unterverzeichnissen:

zmv -Qw '**/*(.)' '$1${(L)2// /-}'
Gilles 'SO - hör auf böse zu sein'
quelle
Mit find -exec prenamemüssen Sie nur das übersetzen basename, sonst funktioniert das nicht richtig, wenn einige Dirnamen Großbuchstaben enthalten. Nicht alle Lösungen verhalten sich für Nicht-ASCII-Buchstaben gleich.
Stéphane Chazelas
@StephaneChazelas Richtig, ich wollte hinzufügen, -maxdepth 1da das ursprüngliche Skript in der Frage rekursiv war, aber ich habe es vergessen. Die Antwort ist lang genug, ich halte mich an ASCII-Buchstaben wie die Frage.
Gilles 'SO - hör auf böse zu sein'
Beachten Sie auch , dass traditionelle SysV trdie erforderlich [...], so kann man schreiben möchte kann es tr '[A-Z]' '[a-z]'Tragbarkeit zu verbessern (als zusätzliche [, ]nicht schaden für den BSD / POSIX-Stil tr).
Stéphane Chazelas
3

Das Hauptproblem ist, dass Sie nicht über Ihre Dateien iterieren: Sie iterieren über eine einzelne Zeichenfolge "ls". Sie wollten wahrscheinlich Backticks anstelle von einfachen Anführungszeichen verwenden. Trotzdem nicht analysierenls .

Sie müssen auch Ihre Variablen zitieren, insbesondere da Sie wissen, dass sie Leerzeichen enthalten.

Nur in Bash können Sie dies tun:

declare -l lc    # whatever is stored in $lc is automatically lower cased
for x in *; do
    lc=${x//[[:space:]]/}  # replace all whitespace with nothing
    [ "$lc" != "$x" ] && mv -- "$x" "$lc"
done
Glenn Jackman
quelle
2

Möglicherweise haben Sie Probleme mit dem Befehl "mv" und Leerzeichen. Ich habe die Originaldatei in doppelte Anführungszeichen gesetzt.

Dieses Skript hat bei mir funktioniert.

#!/bin/bash

for f in *
do
    g=$(echo $f | sed -e 's/\s/\-/g' | tr A-Z a-z)
    mv "$f" $g
done
TP
quelle
2

In Bash 4 konvertiert $ {var ,,} var in Kleinbuchstaben:

for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done
Lri
quelle