Dateien umbenennen, um ein Suffix hinzuzufügen

13

Ich benötige einen Befehl, um alle Dateien im aktuellen Arbeitsverzeichnis so umzubenennen, dass der neue Dateiname mit dem alten identisch ist, jedoch ein Suffix enthält, das der Anzahl der Zeilen der Originaldateien entspricht (z. B. wenn die Datei f10 enthält) Zeilen dann sollte es umbenannt werden f_10).

Hier ist mein (nicht funktionierender) Versuch:

 linenum=$(wc -l); find * -type f | grep -v sh | rename 's/^/ec/'*
Martin Yeboah
quelle
Sie könnten das zu einer Antwort machen! (Wenn es den vorhandenen Antworten etwas Neues hinzufügt - aber es sieht so aus)
Volker Siegel

Antworten:

13

Wie wäre es mit:

for f in *; do mv "$f" "$f"_$(wc -l < "$f"); done

Beispielsweise:

$ wc -l *
 10 file1
 40 file2
100 file3
$ ls
file1_10  file2_40  file3_100

Wenn Sie Erweiterungen behalten möchten (falls vorhanden), verwenden Sie stattdessen Folgendes:

for f in *; do 
    ext=""; 
    [[ $f =~ \. ]] && ext="."${f#*.}; 
    mv "$f" "${f%%.*}"_$(wc -l < "$f")$ext; 
done
terdon
quelle
Vielen Dank, Terdon! Diese Befehlszeile scheint die Arbeit zu erledigen, aber wie kann ich den Punkt (.) in (_) ändern? Da sich mein Skript im Ordner befindet, wird es auch umbenannt, sodass mein Skript einmal ausgeführt werden kann (da sich der Skriptname ändert ...)
Martin Yeboah
@MartinYeboah siehe aktualisierte Antwort für die _. Was das Drehbuch betrifft, welches Drehbuch? Das sollte von der Kommandozeile ausgeführt werden, es wird kein Skript benötigt. Wenn Sie nur möchten, dass es mit einigen Dateien übereinstimmt, ändern Sie in *zum Beispiel den Wert in in *txtoder so.
Terdon
Vielen Dank;) Ich habe total vergessen, dass es von der Kommandozeile ausgeführt werden sollte :)
Martin Yeboah
Ich denke, wc -l < $fanstatt des grep wäre es einfacher zu verstehen (und vielleicht besser durchzuführen, aber ich habe das nicht überprüft).
musiKk
@musiKk ja, aber da die anderen Antworten verwenden wc, wollte ich einen anderen Ansatz zeigen. Aber okay, fair genug.
Terdon
10

Sie können diesen einen Liner ausprobieren:

find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;
  • Dies findet alle Dateien im aktuellen Arbeitsverzeichnis ( find . -maxdepth 1 -type f)

  • Dann führen wir eine Shell-Instanz über die gefundenen Dateien aus, um die Dateien umzubenennen und die Anzahl der Zeilen anzufügen.

Beispiel:

$ ls
bar.txt spam.txt foo.txt

$ find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;

$ ls
bar.txt.12 foo.txt.24 spam.txt.7
heemayl
quelle
6

Ein anderer Weg, um die Erweiterung (falls vorhanden) beizubehalten rename:

for f in *; do rename -n "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done

Wenn das Ergebnis das erwartete ist, entfernen Sie die -nOption:

for f in *; do rename "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done
kos
quelle
renamegreat =) +1
AB
1
Würde das nicht auf abcd scheitern? Ändern Sie die zweite Erfassungsgruppe in (\.?[^\.]+).
ps95
@ prakharsingh95 Das stimmt, ist aber auch (\.?[^\.]+)nicht in Ordnung, da es nur bis zum zweiten Punkt passt und nicht mit einem Dateinamen mit aufeinanderfolgenden Punkten übereinstimmt oder mit einem Punkt endet. Es ist keine einfache Lösung schwer, der einzige Weg scheint zwei Substitutionen zu machen.
Kos
@kos du bist richtig. Wie wäre es mit ([^ \.] +). *? ([^ \.] + $) Ich bin mir nicht sicher, ob es optimal ist. Arbeitsgeige: regex101.com/r/rQ9lX1/1
ps95
1
@ prakharsingh95 du willst nicht entkommen . in Zeichenklassen .
Terdon
5

Verwenden von find:

find . -maxdepth 1 -type f -print0 | while read -d $'\0' f; do mv "$f" "$f"_$(grep -c . "$f"); done

Beispiel

% wc -l *
  3 doit
  5 foo

% find . -maxdepth 1 -type f -print0 | while read -d $'\0' f; do mv "$f" "$f"_$(grep -c . "$f"); done

% wc -l *                         
  3 doit_3
  5 foo_5
AB
quelle
Warum das -name "*"? Reste von Tests? ;)
Kos
1
@kos Alte Angewohnheit: \
AB
3

Nur zum Spaß und kichert eine Lösung mit rename. Da renamees sich um ein Perl-Tool handelt, das eine beliebige Zeichenfolge akzeptiert, die ausgewertet wird, können Sie alle Arten von Spielereien ausführen. Eine Lösung, die zu funktionieren scheint, ist die folgende:

rename 's/.*/open(my $f, "<", $_);my $c=()=<$f>;$_."_".$c/e' *
musik
quelle
Interessante Idee +1
AB
2

Das folgende Skript behandelt mehrere Fälle: den einzelnen Punkt und die Erweiterung (file.txt), mehrere Punkte und Erweiterungen (file.1.txt), aufeinanderfolgende Punkte (file..foobar.txt) und Punkte im Dateinamen (file. Or.) Datei..).

Das Drehbuch

#!/bin/bash
# Author: Serg Kolo
# Date:  June 25,2015
# Description: script to rename files to file_numlines
# written for http://askubuntu.com/q/640430/295286

# Where are the files ?
WORKINGDIR=/home/xieerqi/substitutions
# Where do you want them to go ?
OUTPUTDIR=/home/xieerqi/substitutions/output

for file in $WORKINGDIR/* ;do 
    FLAG=0
    EXT=$(printf "%s" "$file" | awk -F'.' '{printf "%s",$NF }' )  # extension, last field of dot-separated string
    # EXT="${file##*.}" # Helio's advice is to use parameter expansion, but I dont know how to use it
    if [ -z $EXT ]; then # we have a dot at the end case file. or something
        # so we gotta change extension and filename
        EXT=""
        FILENAME=$(printf "%s" "$file" | awk -F '/' '{ print $NF}' )
        # set flag for deciding how to rename
        FLAG=1
    else
        FILENAME=$( printf "%s" "$file" | awk -F '/' -v var=$EXT '{gsub("."var,"");print $NF}'   ) # filename, without path, lst in
    fi

    NUMLINES=$(wc -l "$file" | awk '{print $1}') # line count

    if [ $FLAG -eq 0 ];then
         echo "$file" renamed as "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES"."$EXT"
        # cp "$file" "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES"."$EXT" # uncomment when necessary
    else
        echo "$file" renamed as "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES""$EXT"
        # cp "$file" "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES""$EXT" # uncomment when necessary
    fi

    #printf "\n"

done

Skript in Aktion

$./renamer.sh                                                                                                           
/home/xieerqi/substitutions/file. renamed as /home/xieerqi/substitutions/output/file._0
/home/xieerqi/substitutions/file.. renamed as /home/xieerqi/substitutions/output/file.._0
/home/xieerqi/substitutions/file.1.jpg renamed as /home/xieerqi/substitutions/output/file.1_3.jpg
/home/xieerqi/substitutions/file.1.test.jpg renamed as /home/xieerqi/substitutions/output/file.1.test_3.jpg
/home/xieerqi/substitutions/file.1.test.txt renamed as /home/xieerqi/substitutions/output/file.1.test_2.txt
/home/xieerqi/substitutions/file.1.txt renamed as /home/xieerqi/substitutions/output/file.1_2.txt
/home/xieerqi/substitutions/file.2.jpg renamed as /home/xieerqi/substitutions/output/file.2_3.jpg
/home/xieerqi/substitutions/file.2.test.jpg renamed as /home/xieerqi/substitutions/output/file.2.test_3.jpg
/home/xieerqi/substitutions/file.2.test.txt renamed as /home/xieerqi/substitutions/output/file.2.test_2.txt
/home/xieerqi/substitutions/file.2.txt renamed as /home/xieerqi/substitutions/output/file.2_2.txt
/home/xieerqi/substitutions/foo..bar.txt renamed as /home/xieerqi/substitutions/output/foo..bar_4.txt

Beachten Sie, dass die Datei keine Zeilen enthält. und file .., daher ist die Zeilenzahl 0

Besonderer Dank geht an Terdon und Helio für die Überprüfung des Skripts und die vorgeschlagenen Änderungen

Sergiy Kolodyazhnyy
quelle
2

Ein anderer Bash-Weg, der mit @Helio im Chat entwickelt wurde :

for file in *
do
    echo "$file"
    [[ -f "$file" ]] || continue
    [[ $file =~ (.*)(\.[^.]+)$ ]]
    cp "$file" "output/${BASH_REMATCH[1]:-$file}_$(wc -l < "$file")${BASH_REMATCH[2]}"
done

Der seltsam aussehende Monokel-Typ mit einem verkümmerten zweiten Kopf ( (.*)(\.[^.]+)$) sollte nur zu den richtigen Erweiterungen passen ( .foo, nicht.. ) . Wenn es keine Erweiterung gibt, ist das BASH_REMATCHArray leer. Wir können dies ausnutzen, indem wir einen Standardwert für den Dateinamen verwenden ${BASH_REMATCH[1]:-$file}und die Erweiterung so verwenden, wie sie ist.

Um mit Punktdateien umzugehen , könnten Sie find, wie von Terdon und Helio vorgeschlagen, verwenden .

find -maxdepth 1 -type f -printf '%P\0' | 
while IFS= read -r -d '' file
do
    [[ $file =~ (.*)(\.[^.]+)$ ]]
    cp "$file" "output/${BASH_REMATCH[1]:-$file}_$(wc -l < "$file")${BASH_REMATCH[2]}"
done
muru
quelle
+1: Ich könnte keine Erklärung für den Befehl abgeben. ;-)
Helio