Добавить строки wc к имени файла

Название говорит само за себя. Мне удалось получить только строки с этим:

lines=$(wc file.txt | awk {'print $1'});

Но я мог бы использовать помощь, добавляя это к имени файла. Бонусные баллы за то, что показали мне, как зациклить это на всех файлах .txt в текущем каталоге.


person Andrew Hall    schedule 25.01.2017    source источник
comment
Вы говорите о добавлении вывода wc в конец имени файла или в конец содержимого файла? отредактируйте свой вопрос, включив в него краткие, проверяемые образцы входных данных и ожидаемые результаты. Я поражен, что вы получили 4 голоса за вопрос без примера! Я видел несколько вопросов, когда люди помещали разделитель сценария 's внутри сценария - (awk {'foo'} вместо awk '{foo}') - откуда, черт возьми, ЭТА идея???   -  person Ed Morton    schedule 25.01.2017


Ответы (9)


find -name '*.txt' -execdir bash -c \
  'mv -v "$0" "${0%.txt}_$(wc -l < "$0").txt"' {} \;

где

  • команда bash выполняется для каждого (\;) совпадающего файла;
  • {} заменяется текущим обрабатываемым именем файла и передается сценарию в качестве первого аргумента ($0);
  • ${0%.txt} удаляет кратчайшее совпадение .txt с конца строки (см. официальный скрипт Bash руководство);
  • wc -l < "$0" печатает только количество строк в файле (см. ответы на этот вопрос, например)

Пример вывода:

'./file-a.txt' -> 'file-a_5.txt'
'./file with spaces.txt' -> 'file with spaces_8.txt'
person Ruslan Osmanov    schedule 25.01.2017
comment
Профессиональный внешний вид. Кроме того, только тот, который использует wc -l. ++ - person James Brown; 25.01.2017
comment
Вы должны заменить $(basename "$0" .txt) на ${0%.txt}: basename определенно бесполезен и неэффективен в этом случае! а также, возможно, добавить -type f в предикаты find. - person gniourf_gniourf; 25.01.2017
comment
@gniourf_gniourf, действительно. Спасибо. - person Ruslan Osmanov; 25.01.2017
comment
Отличное решение @RuslanOsmanov. Что удаляет начальные пробелы из вывода wc -l выше? - person codeforester; 27.01.2017
comment
@codeforester, насколько я вижу, современная реализация wc не печатает начальные пробелы для первого счетчика (и особенно для единственного счетчика): git.savannah.gnu.org/cgit/coreutils.git/tree/src/ - person Ruslan Osmanov; 27.01.2017

Вы можете использовать команду rename, которая на самом деле является сценарием Perl, следующим образом:

rename --dry-run 'my $fn=$_; open my $fh,"<$_"; while(<$fh>){}; $_=$fn; s/.txt$/-$..txt/' *txt

Пример вывода

'tight_layout1.txt' would be renamed to 'tight_layout1-519.txt'
'tight_layout2.txt' would be renamed to 'tight_layout2-1122.txt'
'tight_layout3.txt' would be renamed to 'tight_layout3-921.txt'
'tight_layout4.txt' would be renamed to 'tight_layout4-1122.txt'

Если вам нравится то, что он говорит, удалите --dry-run и запустите снова.

Скрипт подсчитывает строки в файле без использования каких-либо внешних процессов, а затем переименовывает их по вашему запросу, также без использования каких-либо внешних процессов, поэтому он достаточно эффективен.

Или, если вы хотите вызвать внешний процесс для подсчета строк и избежать описанного выше метода Perl:

rename --dry-run 's/\.txt$/-`grep -ch "^" "$_"` . ".txt"/e' *txt
person Mark Setchell    schedule 25.01.2017
comment
Проблема с rename в том, что он встречается во многих вариантах. Версия, поставляемая с Perl, удобна, другие гораздо менее мощны. - person hek2mgl; 26.01.2017
comment
@ hek2mgl прав. Например, в моей установке Gentoo rename происходит из пакета sys-apps/util-linux (kernel.org/ pub/linux/utils/util-linux) и, к сожалению, не поддерживает Perl. И версия Perl предоставляется dev-perl/rename как perl-rename исполняемый файл. - person Ruslan Osmanov; 26.01.2017

Используйте команду переименовать

for file in *.txt; do 
 lines=$(wc ${file} | awk {'print $1'});
 rename s/$/${lines}/ ${file}
done
person user13107    schedule 25.01.2017

#/bin/bash

files=$(find . -maxdepth 1 -type f -name '*.txt' -printf '%f\n')
for file in $files; do
    lines=$(wc $file | awk {'print $1'});
    extension="${file##*.}"
    filename="${file%.*}"
    mv "$file" "${filename}${lines}.${extension}"
done

Вы можете настроить максимальную глубину соответственно.

person Farhad Farahi    schedule 25.01.2017
comment
Это сломается, если в каком-либо из файлов есть пробелы или подстановочные знаки - проблема разделения слов. - person codeforester; 25.01.2017

вы также можете сделать так:

for file in "path_to_file"/'your_filename_pattern'
    do
      lines=$(wc $file | awk {'print $1'})
      mv $file $file'_'$lines
    done

пример:

    for file in /oradata/SCRIPTS_EL/text*
    do
        lines=$(wc $file | awk {'print $1'})
        mv $file $file'_'$lines
    done
person Yeasir Arafat Majumder    schedule 25.01.2017

Это сработает, но определенно есть более элегантные способы.

for i in *.txt; do
  mv "$i" ${i/.txt/}_$(wc $i | awk {'print $1'})_.txt; 
done

Result поместит номера строк перед .txt. Нравится:

file1_1_.txt 
file2_25_.txt
person Reuben L.    schedule 25.01.2017
comment
Вы должны сделать цикл for с глобусом. Не трубить ls - person dawg; 25.01.2017
comment
Отредактировано. Спасибо. - person Reuben L.; 25.01.2017
comment
Не могли бы вы объяснить мне причину этого? Спасибо - person Reuben L.; 25.01.2017
comment
@РубенЛ. версия ls разветвляет ненужный процесс, подстановка *.txt в список файлов происходит полностью внутри оболочки - более эффективно. Кроме того, некоторые люди, использующие версию ls, могли бы испортить это из-за вещей в своей среде, например alias ls='ls -F --color=always .... - person DouglasDD; 25.01.2017
comment
@РубенЛ. И менее подвержен ошибкам и более идиоматичен и и и - person dawg; 25.01.2017
comment
Также, возможно, вы захотите заключить в двойные кавычки имя файла назначения (или, по крайней мере, его часть "${i/....") - person DouglasDD; 25.01.2017
comment
Вам не нужен башизм ${i/.txt} здесь, ${i%.txt} тоже сделает свою работу (и не будет ломаться для имени файла формы file.txt.lalala.txt); как сказано выше, вы должны заключать в кавычки все расширения переменных; и если вы используете wc -l < "$i", awk становится бесполезным (и ваш скрипт будет более эффективным и не будет ломаться с именами файлов, содержащими символы новой строки). - person gniourf_gniourf; 25.01.2017

Вы можете использовать grep -c '^' для получения количества строк вместо wc и awk:

for file in *.txt; do
  [[ ! -f $file ]] && continue # skip over entries that are not regular files
  #
  # move file.txt to file.txt.N where N is the number of lines in file
  #
  # this naming convention has the advantage that if we run the loop again,
  # we will not reprocess the files which were processed earlier
  mv "$file" "$file".$(grep -c '^' "$file")
done
person codeforester    schedule 25.01.2017

{ linecount[FILENAME] = FNR }
END {
    linecount[FILENAME] = FNR
    for (file in linecount) {
        newname = gensub(/\.[^\.]*$/, "-"linecount[file]"&", 1, file)
        q = "'"; qq = "'\"'\"'"; gsub(q, qq, newname)
        print "mv -i -v '" gensub(q, qq, "g", file) "' '" newname "'"
    }
    close(c)
}

Сохраните приведенный выше скрипт awk в файле, скажем, wcmv.awk, запустите его следующим образом:

awk -f wcmv.awk *.txt

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

awk -f wcmv.awk *.txt | sh

Как и со всеми необратимыми пакетными операциями, будьте осторожны и выполняйте команды, только если они выглядят нормально.

person pii_ke    schedule 25.01.2017
comment
Плохой дизайн с самого начала: вы смешиваете код и данные! что, если имя файла содержит кавычки? - person gniourf_gniourf; 25.01.2017
comment
@gniourf_gniourf Я думаю, что одинарные кавычки в именах файлов теперь не вызовут проблем. И что ж, теперь этот код выглядит достойно. :П - person pii_ke; 25.01.2017

 awk '
  BEGIN{ for ( i=1;i<ARGC;i++ ) Files[ARGV[i]]=0 }

  {Files[FILENAME]++}

  END{for (file in Files) {
        # if( file !~ "_" Files[file] ".txt$") {

           fileF=file;gsub( /\047/, "\047\"\047\"\047", fileF)
           fileT=fileF;sub( /.txt$/, "_" Files[file] ".txt", fileT)

           system( sprintf( "mv \047%s\047 \047%s\047", fileF, fileT))

        #   }
        }
     }' *.txt

Еще один способ с помощью awk упростить управление вторым циклом, предоставляя больше контроля над именем (например, избегая того, что внутри уже есть счет из предыдущего цикла)

Благодаря хорошему замечанию @gniourf_gniourf:

  • имя файла с пробелом внутри возможно
  • крошечный код теперь тяжеловат для такой маленькой задачи
person NeronLeVelu    schedule 25.01.2017
comment
Плохой дизайн с самого начала: вы _смешиваете код и данные! что делать, если имя файла содержит кавычки? - person gniourf_gniourf; 25.01.2017
comment
Вы правы, я забыл предположить это. Есть вторая проблема, пустые файлы, которые никогда не достигаются (используя ARGV, решить эту проблему, но создать проблему с пробелами в именах, ...), попробовать несколько советов, но создать газовую фабрику (модульную, но неадекватную для такого небольшого запроса). Ставлю последнюю версию с допущением пока лучше - person NeronLeVelu; 25.01.2017
comment
адаптированный код для пробела и одинарной кавычки внутри имени файла - person NeronLeVelu; 26.01.2017