Renomeando arquivos para adicionar um sufixo

13

Eu preciso de um comando para renomear todos os arquivos no diretório de trabalho atual, de forma que o novo nome do arquivo seja o mesmo do antigo, mas incluindo um sufixo correspondente ao número de linhas dos arquivos originais (por exemplo, se o arquivo ftiver 10 linhas, em seguida, ele deve ser renomeado para f_10).

Aqui está minha tentativa (que não funciona):

 linenum=$(wc -l); find * -type f | grep -v sh | rename 's/^/ec/'*
Martin Yeboah
fonte
Você poderia fazer disso uma resposta! (Se ele acrescenta algo de novo para as respostas já existentes - mas parece que ele faz)
Volker Siegel

Respostas:

13

E se:

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

Por exemplo:

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

Se você deseja manter as extensões (se houver), use isso:

for f in *; do 
    ext=""; 
    [[ $f =~ \. ]] && ext="."${f#*.}; 
    mv "$f" "${f%%.*}"_$(wc -l < "$f")$ext; 
done
Terdon
fonte
Graças à terdon! essa linha de comando parece concluir o trabalho, mas como posso alterar o ponto (.) para (_)? Além disso, como o meu script está dentro da pasta, ele é renomeado demais e, portanto, permite que o meu script para ser executado uma vez (porque o nome script muda ...)
Martin Yeboah
@MartinYeboah veja a resposta atualizada para o _. Quanto ao script, qual script? Isso deve ser executado na linha de comando, não há necessidade de um script. Se você deseja que ele corresponda apenas a alguns arquivos, altere in *para, por exemplo, in *txtou algo assim.
terdon
thanks a lot;) Eu esqueci completamente que ele deve ser executado a partir da linha de comando :)
Martin Yeboah
Eu acho que ao wc -l < $finvés do grep seria mais fácil entender (e talvez seja melhor executar, mas eu não verifiquei isso).
musikk
@musiKk sim, mas como as outras respostas estão sendo usadas wc, eu queria mostrar uma abordagem diferente. Mas tudo bem, é justo.
terdon
10

Você pode tentar este forro:

find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;
  • Isso encontrará todos os arquivos no diretório de trabalho atual ( find . -maxdepth 1 -type f)

  • Em seguida, estamos executando uma instância do shell sobre os arquivos encontrados para renomear os arquivos para acrescentar o número de linhas.

Exemplo:

$ 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
heemail
fonte
6

Outra maneira que preserva a extensão (se presente) usando rename:

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

Se o resultado for o esperado, remova a -nopção:

for f in *; do rename "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done
kos
fonte
renameÓtimo =) +1
AB
1
Isso não falharia no abcd? Mude o segundo grupo de captura para (\.?[^\.]+).
ps95 24/06
@ prakharsingh95 Isso é verdade, no entanto, (\.?[^\.]+)também não está ok porque corresponderá apenas até o segundo ponto e não corresponderá a um nome de arquivo com pontos consecutivos ou terminará com um ponto independentemente. Não é uma solução fácil, difícil, a única maneira parece fazer duas substituições.
kos
@ kos você está correto. Que tal ([^ \.] +). *? ([^ \.] + $) Não tenho certeza sobre a otimização. Violino de trabalho: regex101.com/r/rQ9lX1/1
ps95
1
@ prakharsingh95 você não quer fugir das .classes de personagens.
terdon
5

Usando find:

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

Exemplo

% 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
fonte
Por que o -name "*"? Sobras dos testes? ;)
kos
1
@kos Antigo hábito: \
AB
3

Apenas por diversão e ri de uma solução com rename. Como renameé uma ferramenta Perl que aceita uma sequência arbitrária que é avaliada, você pode fazer todo tipo de travessuras. Uma solução que parece funcionar é a seguinte:

rename 's/.*/open(my $f, "<", $_);my $c=()=<$f>;$_."_".$c/e' *
musiKk
fonte
Ideia interessante +1
AB
2

O script abaixo abrange vários casos: o único ponto e extensão (arquivo.txt), vários pontos e extensões (arquivo.1.txt), pontos consecutivos (arquivo..foobar.txt) e pontos no nome do arquivo (arquivo. Ou Arquivo..).

O 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 em ação

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

Observe que não há linhas no arquivo. e arquivo .., portanto, a contagem de linhas é 0

Agradecimentos especiais a Terdon e Helio pela revisão do script e edições sugeridas

Sergiy Kolodyazhnyy
fonte
2

Outra maneira bash, desenvolvida com @Helio no 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

O cara monocular de aparência estranha com uma segunda cabeça atrofiada ( (.*)(\.[^.]+)$) deve corresponder apenas a extensões apropriadas ( .foo, não ..). Se não houver extensão, a BASH_REMATCHmatriz estará vazia. Podemos tirar vantagem disso usando um valor padrão para o nome do arquivo ${BASH_REMATCH[1]:-$file}e apenas usando a extensão como está.

Para lidar com arquivos de ponto, você pode usar find, conforme sugerido por terdon e 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
fonte
+1: eu não seria capaz de fazer uma explicação para o comando. ;-)
Helio