Переименование файлов для добавления суффикса

13

Мне нужна команда для переименования всех файлов в текущем рабочем каталоге таким образом, чтобы новое имя файла было таким же, как старое, но с суффиксом, соответствующим количеству строк исходных файлов (например, если файл fимеет 10 линии, то он должен быть переименован в f_10).

Вот моя (нерабочая) попытка:

 linenum=$(wc -l); find * -type f | grep -v sh | rename 's/^/ec/'*
Мартин Йебоа
источник
Вы могли бы сделать это ответ! (Если он добавляет что-то новое к существующим ответам - но похоже, что так и есть)
Volker Siegel,

Ответы:

13

Как насчет:

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

Например:

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

Если вы хотите сохранить расширения (если они есть), используйте вместо этого:

for f in *; do 
    ext=""; 
    [[ $f =~ \. ]] && ext="."${f#*.}; 
    mv "$f" "${f%%.*}"_$(wc -l < "$f")$ext; 
done
terdon
источник
Спасибо, Тердон! эта командная строка, кажется, выполняет работу, но как я могу изменить точку (.) на (_)? Кроме того, поскольку мой сценарий находится внутри папки, он также переименован и, следовательно, позволяет выполнять мой сценарий один раз (так как имя сценария изменяется ...)
Мартин Йебоа,
@MartinYeboah см обновленный ответ для _. Что касается сценария, какой сценарий? Это должно быть запущено из командной строки, нет необходимости в сценарии. Если вы хотите, чтобы он совпадал только с некоторыми файлами, измените in *значение, например, на in *txtчто-то другое.
тердон
большое спасибо;) Я полностью забыл, что это должно быть выполнено из командной строки :)
Martin Yeboah
Я думаю, что wc -l < $fвместо grep было бы легче понять (и, возможно, лучше выполнить, но я не проверял это).
MusiKk
@musiKk Да, но так как другие ответы используются wc, я хотел показать другой подход. Но хорошо, достаточно справедливо.
тердон
10

Вы можете попробовать этот лайнер:

find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;
  • Это найдет все файлы в текущем рабочем каталоге ( find . -maxdepth 1 -type f)

  • Затем мы запускаем экземпляр оболочки над найденными файлами, чтобы переименовать файлы и добавить число строк.

Пример :

$ 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
источник
6

Другой способ, который сохраняет расширение (если присутствует), используя rename:

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

Если результат ожидаемый, уберите -nопцию:

for f in *; do rename "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done
кос
источник
renameотлично =) +1
AB
1
Разве это не сработает на abcd? Измените вторую группу захвата на (\.?[^\.]+).
PS95
@ prakharsingh95 Это правда, однако (\.?[^\.]+)это тоже не нормально, потому что оно будет соответствовать только до второй точки и не будет совпадать с именем файла с последовательными точками или независимо от точки. Это непростое решение, кажется, единственный способ сделать две замены.
Кос
@kos ты прав. Как насчет ([^ \.] +). *? ([^ \.] + $) Я не уверен в оптимальности. Рабочая скрипка: regex101.com/r/rQ9lX1/1
PS95
1
@ prakharsingh95 вы не хотите убегать .внутри классов персонажей.
тердон
5

Использование find:

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

пример

% 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
источник
Почему то -name "*"? Остаток от испытаний? ;)
Кос
1
@kos Старая привычка: \
AB
3

Просто для удовольствия и хихикает решение с rename. Так renameкак это инструмент Perl, который принимает произвольную строку, которая является eval'd, вы можете делать все виды махинаций. Решение, которое кажется эффективным, заключается в следующем:

rename 's/.*/open(my $f, "<", $_);my $c=()=<$f>;$_."_".$c/e' *
Musikk
источник
Интересная идея +1
AB
2

Сценарий, приведенный ниже, охватывает несколько случаев: одну точку и расширение (file.txt), несколько точек и расширений (file.1.txt), последовательные точки (file..foobar.txt) и точки в имени файла (file. Or файл..).

Сценарий

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

Сценарий в действии

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

Обратите внимание, что в файле нет строк. и файл .., следовательно, количество строк равно 0

Отдельное спасибо Тердону и Хелио за рецензирование сценария и предложенные изменения

Сергей Колодяжный
источник
2

Еще один способ, разработанный с помощью @Helio в чате :

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

Странно выглядящий парень с односторонними головами ( (.*)(\.[^.]+)$) должен соответствовать только правильным расширениям ( .fooне ..). Если расширения нет, то BASH_REMATCHмассив будет пустым. Мы можем воспользоваться этим, используя значение по умолчанию для имени файла ${BASH_REMATCH[1]:-$file}и просто используя расширение как есть.

Для работы с точечными файлами вы можете использовать find, как это предложили Тердон и Хелио .

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
Мур
источник
+1: я не смогу сделать объяснение для команды. ;-)
Helio