Renommer des fichiers pour ajouter un suffixe

13

J'ai besoin d'une commande pour renommer tous les fichiers dans le répertoire de travail actuel, de manière à ce que le nouveau nom de fichier soit le même que l'ancien, mais en incluant un suffixe correspondant au nombre de lignes des fichiers d'origine (par exemple si le fichier fa 10 alors il doit être renommé f_10).

Voici ma tentative (non fonctionnelle):

 linenum=$(wc -l); find * -type f | grep -v sh | rename 's/^/ec/'*
Martin Yeboah
la source
Vous pourriez en faire une réponse! (Si cela ajoute quelque chose de nouveau aux réponses existantes - mais il semble que ce soit le cas)
Volker Siegel

Réponses:

13

Que diriez-vous:

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

Par exemple:

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

Si vous souhaitez conserver les extensions (le cas échéant), utilisez-les à la place:

for f in *; do 
    ext=""; 
    [[ $f =~ \. ]] && ext="."${f#*.}; 
    mv "$f" "${f%%.*}"_$(wc -l < "$f")$ext; 
done
terdon
la source
Merci Terdon! cette ligne de commande semble faire le travail, mais comment puis-je changer le point (.) en (_)? De plus, comme mon script est à l'intérieur du dossier, il est également renommé et permet donc à mon script d'être exécuté une fois (car le nom du script change ...)
Martin Yeboah
@MartinYeboah voir la réponse mise à jour pour le _. Quant au script, quel script? Cela devrait être exécuté à partir de la ligne de commande, il n'y a pas besoin de script. Si vous souhaitez uniquement qu'il corresponde à certains fichiers, remplacez le in *par, par exemple, in *txtou quelque chose.
terdon
merci beaucoup;) J'ai totalement oublié qu'il devrait être exécuté à partir de la ligne de commande :)
Martin Yeboah
Je pense wc -l < $fqu'au lieu de la grep serait plus facile à comprendre (et peut-être mieux à effectuer mais je n'ai pas vérifié cela).
musiKk
@musiKk oui, mais comme les autres réponses utilisent wc, je voulais montrer une approche différente. Mais OK, assez bien.
terdon
10

Vous pouvez essayer cette doublure:

find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;
  • Cela trouvera tous les fichiers dans le répertoire de travail actuel ( find . -maxdepth 1 -type f)

  • Ensuite, nous exécutons une instance de shell sur les fichiers trouvés pour renommer les fichiers pour ajouter le nombre de lignes.

Exemple :

$ 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
la source
6

Une autre manière qui préserve l'extension (si présente) en utilisant rename:

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

Si le résultat est celui attendu, supprimez l' -noption:

for f in *; do rename "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done
kos
la source
renamegrand =) +1
AB
1
Cela n'échouerait-il pas sur abcd? Remplacez le deuxième groupe de capture par (\.?[^\.]+).
ps95
@ prakharsingh95 C'est vrai, mais ce (\.?[^\.]+)n'est pas correct non plus car il ne correspondra qu'au deuxième point et ne correspondra pas à un nom de fichier avec des points consécutifs ou se terminant par un point malgré tout. Ce n'est pas une solution facile difficile, la seule façon semble faire deux substitutions.
kos
@kos vous avez raison. Que diriez-vous ([^ \.] +). *? ([^ \.] + $) Je ne suis pas sûr de l'optimalité. Violon de travail: regex101.com/r/rQ9lX1/1
ps95
1
@ prakharsingh95 vous ne voulez pas vous échapper à l' .intérieur des classes de personnages.
terdon
5

En utilisant find:

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

Exemple

% 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
UN B
la source
Pourquoi -name "*"? Des restes de tests? ;)
kos
1
@kos Vieille habitude: \
AB
3

Juste pour le plaisir et glousse une solution avec rename. Puisqu'il renames'agit d'un outil Perl qui accepte une chaîne arbitraire qui est évaluée, vous pouvez faire toutes sortes de manigances. Une solution qui semble fonctionner est la suivante:

rename 's/.*/open(my $f, "<", $_);my $c=()=<$f>;$_."_".$c/e' *
musiKk
la source
Idée intéressante +1
AB
2

Le script ci-dessous couvre plusieurs cas: le point unique et l'extension (file.txt), plusieurs points et extensions (file.1.txt), des points consécutifs (file..foobar.txt) et des points dans le nom de fichier (file. Or fichier..).

Le script

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

Script en action

$./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

Notez qu'il n'y a pas de lignes dans le fichier. et fichier .., donc le nombre de lignes est 0

Un merci spécial à Terdon et Helio pour avoir révisé le script et les modifications suggérées

Sergiy Kolodyazhnyy
la source
2

Un autre moyen bash, développé avec @Helio dans le chat :

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

Le type monocle à l'aspect étrange avec une deuxième tête rabougrie ( (.*)(\.[^.]+)$) ne devrait correspondre qu'à des extensions appropriées ( .foo, pas ..). S'il n'y a pas d'extension, le BASH_REMATCHtableau sera vide. Nous pouvons en profiter en utilisant une valeur par défaut pour le nom de fichier ${BASH_REMATCH[1]:-$file}et en utilisant simplement l'extension telle quelle.

Pour gérer les fichiers dot, vous pouvez utiliser find, comme suggéré par terdon et Helio .

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
la source
+1: Je ne pourrais pas faire d'explication pour la commande. ;-)
Helio