Leerzeichen, Bindestriche und Unterstriche in Dateinamen löschen?

10

Was ist ein guter Befehl, um Leerzeichen, Bindestriche und Unterstriche aus allen Dateien in einem Verzeichnis oder ausgewählten Dateien zu löschen?

Ich verwende den folgenden Befehl mit benutzerdefinierten Thunar-Aktionen, um Dateinamen zu löschen:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

Dieser Befehl ersetzt jedoch nur Leerzeichen durch Bindestriche / Bindestriche und Kleinbuchstaben.

Ich habe den folgenden Befehl im Terminal verwendet, um Leerzeichen aus Tausenden von Dateinamen in einem Ordner zu löschen, und es hat ziemlich schnell funktioniert:

 rename "s/ //g" *

Auch hier werden nur Leerzeichen gelöscht und keine Bindestriche / Bindestriche und Unterstriche.

Im Idealfall möchte ich keine Leerzeichen, Bindestriche / Bindestriche und Unterstriche in meinen Dateinamen. Und es wäre großartig, wenn der Befehl mit benutzerdefinierten Thunar-Aktionen für ausgewählte Dateien verwendet werden könnte.

user8547
quelle
2
Ich stelle fest, dass ein Problem, das viele der vorgeschlagenen Lösungen haben, darin besteht, die Existenz des "neuen" Namens nicht ordnungsgemäß zu überprüfen, bevor die Datei erstellt wird. Wenn Sie dies nicht tun, kann dies zu vielen Problemen führen.
mdpc
Ist es möglich, den Befehl von John1024 zu ändern, um dies zu überprüfen?
user8547
@ user8547rename -i "s/[-_ ]//g" *
Sparhawk
Vielen Dank, Sparhawk. Für diejenigen, die dies als benutzerdefinierte Thunar-Aktion verwenden möchten, lautet der Befehl für Thunar übrigens: für Datei in% N; mache mv "$ file" echo $file | sed -e 's/[ _-]//g'; erledigt
user8547

Antworten:

11

Die renamemit dem perlPaket gelieferte Version unterstützt reguläre Ausdrücke:

rename "s/[-_ ]//g" *

Alternative,

rename -i "s/[-_ ]//g" *

Das -iFlag renameverwendet den interaktiven Modus und fragt, ob das Ziel bereits vorhanden ist, anstatt es stillschweigend zu überschreiben.

Perls Umbenennung wird manchmal genannt prename.

Perls Umbenennung versus Umbenennung von util-linux

Auf Debian-ähnlichen Systemen scheint die Umbenennung von Perl die Standardeinstellung zu sein, und die oben genannten Befehle sollten einfach funktionieren.

Bei einigen Distributionen ist das renameDienstprogramm von util-linux die Standardeinstellung. Dieses Dienstprogramm ist vollständig nicht mit Perl kompatibel rename.

  • Alle: Überprüfen Sie zunächst, ob Perls renameunter dem Namen verfügbar ist prename.

  • Debian: Perls Umbenennung sollte die Standardeinstellung sein. Es ist auch als erhältlich prename. Die renameausführbare Datei steht jedoch unter der Kontrolle von /etc/alternativesund könnte daher in etwas anderes geändert worden sein.

  • archlinux: Ausführen pacman -S perl-renameund der Befehl ist verfügbar als perl-rename. Erstellen Sie für einen bequemeren Namen einen Alias. (Hutspitze: ChiseledAbs)

  • Mac OSX Nach dieser Antwort , renamekann auf OSX installiert werden homebrew mit über:

    brew install rename 
  • Direkter Download: rename ist auch bei Perl Monks erhältlich:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename
John1024
quelle
Ich denke, das hängt davon ab, wovon renamedu sprichst. Der von util-linux -2.24.2-1.fc20.x86_64 unterstützt keine regulären Ausdrücke.
Cristian Ciupitu
1
@CristianCiupitu Ich habe gerade auf der Manpage nach der Version der Umbenennung gesucht, die Sie gefunden haben. Basierend auf den Argumenten renamesieht die vom OP verwendete perlVersion wie die Version und nicht wie die util-linuxVersion aus.
John1024
Für die Aufzeichnung ist dies die renameManpage für die Util-Linux- Version. Abgesehen von dieser Notiz ist es wichtig, dass der OP seine Antwort erhielt (und Sie eine positive Bewertung von mir :-D).
Cristian Ciupitu
@CristianCiupitu Danke, dass du das gefunden hast. Zurück bei dir mit +1.
John1024
1
@ John1024 archlinux, aber ich habe herausgefunden wie, geh einfach pacman -S perl-renamedann ich denke du kannst alias.
ChiseledAbs
5

Ich würde alle diese trBefehle durch einen sedErsetzungsbefehl ersetzen , z.

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done
Cristian Ciupitu
quelle
4

Ohne zu zählen mv, brauchen Sie dafür überhaupt keinen externen Prozess - Sie können sie einfach nur bescheißen .

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

Dies bedeutet jedoch einen mvAufruf pro Datei und ist daher wahrscheinlich renamebesser. Dies sollte jedoch funktionieren, wenn nur ein POSIX mvin $PATHund eine POSIX-Shell vorhanden sind.

Also habe ich mir dafür eine Art verrückte Demo ausgedacht. Der Testsatz wird wie folgt generiert:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

Zunächst werde ich als erster anerkennen, dass der obige Befehl Ergebnisse liefert, die auf andere Weise leichter zu erhalten sind. Aber andere Mittel würden wahrscheinlich nicht so gut zeigen, was mit $IFSein wenig (krank?) Fantasie getan werden könnte .

Das erste Bit ist also ziemlich einfach:

  • tee leitet 5 Kopien seiner Eingabe heraus - das heredocument heißt CGEN

  • dd blockiert seine Eingabe durch Zeilenumbrüche mit 90 Bytes pro Block und leitet diese an ...

  • sed\nVerbindet 2 dieser Blöcke mit zwei Ewline-Zeichen, setzt 'die Ergebnisse in einfache Anführungszeichen und stellt die Zeichenfolge touch --für jeden Zeilenzyklus voran, bevor sie an ...

  • sh Das führt dann alle Eingaben als Shell-Befehle aus

Das #CGENbisschen aber ... Nun, kurz ...

  • Die Unterseite printfdruckt 252 0s

  • Der vorletzte erhält 252 ''Null-String-Argumente und druckt für jeden den Inhalt von $ngefolgt von dem String" $i "

  • evalinterpretiert die Argumente des nächsten nach oben, printfbevor die Ergebnisse dieser Interpretation als Oktalzahlen gedruckt werden, denen 2 Backslashes pro Stück vorangestellt sind

  • Der letzte gibt printfdie Bytewerte für diese Oktale 2 gleichzeitig aus, gefolgt von der Zeichenfolge -_ ---___für jedes Paar

  • $nwird mit einer Gleichung initialisiert, die $ibei jeder Auswertung um eins erhöht wird, mit der Ausnahme, dass die Werte 10, 39 oder 47 übersprungen werden ( \newline, 'einfache Anführungszeichen und /Schrägstrich in ASCII-Dezimalzahl).

Das Endergebnis ist ein Verzeichnis mit vielen wirklich hässlichen Dateinamen, die jedes Byte in meinem Zeichensatz von 1 bis 255 enthalten, mit Ausnahme des einfachen Anführungszeichens (nur übersprungen, um eine weitere sed s///Anweisung zu vermeiden ) und des /Schrägstrichs. Diese Dateinamen sehen folgendermaßen aus:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

Jetzt werde ich einige Daten zu diesen Dateien erhalten:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

AUSGABE

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

In Ordnung. Nun endlich zum Handeln:

ifsqz '_ -'
chksqz '_ -'

AUSGABE

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

Erfolg! Sie können selbst sehen:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff
mikeserv
quelle
2
+1 für eine kreative Verwendung von IFS+printf
John1024
@ John1024 - was wirklich Spaß macht:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
Mikesserv
1
new="$(IFS=" -_"; printf %s $1)"gabelt eine Unterschale (außer in ksh93) und hat Probleme mit dem Tailing von Zeilenumbrüchen. Eine andere Option ist zu verwenden IFS=' -_'; set -- $1; IFS=; new="$*"(und ändern Sie Ihre while-Schleife in eine for-Schleife)
Stéphane Chazelas
1
[ -e x ]gibt false zurück, wenn xes sich um einen Symlink zu einer nicht vorhandenen oder nicht zugänglichen Datei handelt.
Stéphane Chazelas
1
Schöne Muschel Kung-Fu!
Gegenmodus
2

Wenn Sie Perl haben, müssen Sie normalerweise umbenennen. du kannst tun:

> type rename
rename is /usr/bin/rename

und zeigen Sie, wie dieses Skript geschrieben ist:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker ([email protected]),
#  from Larry Wall's original script eg/rename from the perl source.
#

Dieses Skript unterstützt das Flag -i nicht (dies ist die Version in meinem System), aber möglicherweise unterstützt Ihr Flag. Was ist mit Argumenten? Das erste sind reguläre Ausdrücke im PCRE-Format. Sie funktionieren wie Filter. Ändern Sie den Eingabenamen in den Ausgabenamen. Liste der Eingabenamen, die Sie mit dem Sternchen '*' angeben. Zum Beispiel tun Sie:

> cd /tmp
> rename 's/ //g' *

in real '*' kann erweitert werden zu:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

Wenn Sie wirklich große Dateien haben, sind Sie in der Falle. Shell wird Ihre Linie länger erweitern, als das System akzeptiert. Dann können Sie eine Problemumgehung mit find oder xargs durchführen. Die Verwendung von 'find' ist ein Problem, da das Umbenennen mehrmals aufgerufen wird, was der Anzahl der Dateien im Verzeichnis entspricht. Verwenden Sie besser xargs mit der Option -r. Ein Umbenennungsaufruf ändert viele Dateien. beispielsweise:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

letztes Problem, was bedeutet es:

's/ //g'

Dies ist ein regulärer Ausdruck zum Ändern von Namen. Nach dem ersten '/' steht das Leerzeichen. Dies wird erkannt und nach dem zweiten '/' durch einen String ersetzt. Aber es gibt eine leere Zeichenfolge, die mit dem dritten '/' endet, dann wird das Leerzeichen durch nichts ersetzt. Option 'g' wiederholt diesen Ausdruck. Der Ausdruck wird von Anfang bis Ende nach allen Namen durchsucht und erkennt alle Leerzeichen.

Aber was ist, wenn Sie ein Tabulatorzeichen oder ein anderes "weißes" Zeichen haben? Es gibt Ersatz für dieses '\ s'. Welche anderen nicht benötigten Charaktere? füge es einfach dem Ausdruck hinzu. Alle schließen mit Klammern, zum Beispiel:

's/[\s_-]//g'

das ist alles. siehst du Ähnlichkeit? Ich denke, Sie sollten man perlrequick und man perlretut lesen, dies erklärt Ihnen (ich hoffe), wie regulärer Ausdruck funktioniert. Sie können den Befehl zum Umbenennen in Ihrem eigenen Skript verwenden, wenn Sie ihn benötigen.

Znik
quelle
1

Die folgende shShell-Schleife entfernt alle Leerzeichen, Unterstriche und Bindestriche aus den Namen der Dateien im aktuellen Verzeichnis, wobei darauf geachtet wird, dass keine vorhandenen Dateien überschrieben werden:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

Für bashund ksh, und etwas ausführlicher mit der Logik:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

Entfernen Sie das, echowenn Sie sicher sind, dass es das tut, was Sie wollen.

Der trBefehl löscht ( -d) alle Zeichen im angegebenen Zeichensatz ( ' _-'). Es ist wichtig, dass der Bindestrich ganz am Anfang oder Ende des Satzes steht, da er sonst als Zeichenbereich interpretiert wird.

Kusalananda
quelle