Скопируйте определенный процент каждого файла в каталоге в новый файл

Это не слишком редко, чтобы иметь инструменты, которые ожидают быть установленными на уровне пользователя. По сути, они не предположат, что можно изменить что-либо непосредственно под /usr. Часто распространено, однако, иметь a ~/bin или ~/usr/bin каталог, где можно включать символьные ссылки на инструменты, которые Вы установили для своего пользователя. Таким образом, что Вы не должны постоянно обновлять a $PATH переменная.

4
06.12.2014, 07:47
3 ответа

Таким образом, создавая один пример для работы из:

root@crunchbang-ibm3:~# echo {0..100} > file1        
root@crunchbang-ibm3:~# cat file1
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

Мы можем получить размер файла в байтах с помощью stat:

root@crunchbang-ibm3:~# stat --printf %s "file1"
294

А затем, используя bc, мы можем умножить размер на . 2

root@crunchbang-ibm3:~# echo "294*.2" | bc
58.8

Однако мы получаем плавающий диск, поэтому давайте преобразовывать его в целое число, так как head ( dd может работать и здесь ):

root@crunchbang-ibm3:~# printf %.0f "58.8" 
59

И, наконец, первые двадцать процентов (плюс-минус один байт) файла1:

root@crunchbang-ibm3:~# head -c "59" "file1" 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Собрав его вместе, мы могли бы сделать нечто подобное

mkdir -p a_new_directory
for f in file*; do
    file_size=$(stat --printf %s "$f")
    percent_size_as_float=$(echo "$file_size*.2" | bc)
    float_to_int=$(printf %.0f "$percent_size_as_float")
    grab_twenty=$(head -c "$float_to_int" "$f")
    new_fn=$(printf "%s_20" "$f") # new name file1_20
    printf "$grab_twenty" > a_new_directory/$new_fn
done

где f является держателем для любых элементов, найденных в каталоге, в котором запущен цикл for, который совпадает с файлом *

, который по завершению работы:

root@crunchbang-ibm3:~# cat a_new_directory/file1_20
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 

обновление (чтобы захватить 20% строк):

Чтобы захватить первые приблизительные 20% строк, мы можем заменить stat --printf %s "$f" на:

wc -l < "$f"

Так как мы используем printf и bc, мы можем эффективно округлить от . 5, однако, если файл длиной всего 1 или 2 строки, он их пропустит. Поэтому мы бы хотели не только округлить, но и по умолчанию, по крайней мере, захватить 1 строку.

6
27.01.2020, 20:46

Использование упомянутых вами инструментов + Найти :
Получите процент строк или байт 1 с Head -n-файлом PERC или Файл PERC ,
где Perc дается ((Count / 5)) ,
Где считается
WC -L <файл или WC -C <файл ,
Наконец, напишите выходные данные в соответствии file_20 .

Примечание. Оператор / раундут до ближайшего целого числа, так что любой файл * с линиями / байтами сочетается <5 (следовательно PERC = 0 ) создаст пустой файл * _20 файл.

Получите первые 20% - линии:

mkdir some_dir_name
find . -maxdepth 1 -iname 'file*' -exec sh -c 'head -n $(( $(wc -l < "$0") / 5 )) "$0" > some_dir_name/"$0"_20' {} \;

Получите первые 20% - байты:

mkdir some_dir_name
find . -maxdepth 1 -iname 'file*' -exec sh -c 'head -c $(( $(wc -c < "$0") / 5 )) "$0" > some_dir_name/"$0"_20' {} \;

1
Обратите внимание, что, в зависимости от макета текста, два метода могут привести к значительному разным результатам, например, Для 10-линии текстового образца:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.


Abstract

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
ut aliquip ex ea commodo consequat. 

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum...

первые 20% от общего количества строк = первые 2 строки:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.

первые 20% от общего количества байтов = первая строка (усеченная):

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
4
27.01.2020, 20:46

Черт. Я написал весь этот большой ответ с помощью тщательно разработанного метода синтаксического анализа архива tar - это было круто. Но я добрался до конца и понял, что в этом нет никакой необходимости. Все, что вам нужно, это sed и небольшая математика оболочки:

set ./file[1-5];i=1 n=;eval "${n:=
}       sed -n  \"$(grep -c '.\|' "$@"|
        sed 's|\(.*\):\(.*\)|\
        $i,$(((\2/5)+(i+=\2)-\2))w \1|
        ')\" <<!$n"'$(cat "$@")'"$n!$n"

Там grep -c подсчитывает строки в любых файлах, которые вы скопировали - я добавил file [1-5 ] - и передает счет в sed , который затем - с небольшой помощью оболочки - пишет свой собственный сценарий. cat обеспечивает ввод через документ.Это потому, что я сомневаюсь в том, что может произойти, если sed откроет и начнет запись в один из файлов, которые cat пытается прочитать в него - также я подозреваю было бы немного лучше обрабатывать буферы, чем канал, в зависимости от размера - но я не слишком ясен в этой части.

Таким образом, все файлы читаются в одном потоке, а w обрабатывает вывод соответствующим образом. Для правильного увеличения номеров файлов требуется небольшая настройка - поэтому grep и eval - ничего страшного. Вот некоторые выходные данные set -x , чтобы показать, что он делает:

+ set ./file1 ./file2 ./file3 ./file4 ./file5
+ i=1 n=
+ + grep -c .\| ./file1 ./file2 ./file3 ./file4 ./file5
        sed s|\(.*\):\(.*\)|\
        $i,$(((\2/5)+(i+=\2)-\2))w \1|

+ eval 
       sed -n  "
        $i,$(((18400/5)+(i+=18400)-18400))w ./file1

        $i,$(((18411/5)+(i+=18411)-18411))w ./file2

        $i,$(((18415/5)+(i+=18415)-18415))w ./file3

        $i,$(((18418/5)+(i+=18418)-18418))w ./file4

        $i,$(((18421/5)+(i+=18421)-18421))w ./file5" <<!
$(cat "$@")
!

+ cat ./file1 ./file2 ./file3 ./file4 ./file5
+ sed -n 
        1,3681w ./file1

        18401,22083w ./file2

        36812,40495w ./file3

        55227,58910w ./file4

        73645,77329w ./file5

Как видите, адреса строк определяются в зависимости от позиции каждого файла в потоке, и они w ritten поскольку они читаются в их соответствующих именах файлов. Однако важно отметить, что при этом не предпринимаются попытки обработать какие-либо непереносимые символы в имени пути - в частности, новые строки в именах путей в этом случае не являются стартером, поскольку команда sed w rite ограничивает имя файла аргументы о новых строках. Ситуацию легко обойти, если необходимо, с помощью ln , если вам это нужно.

Я также должен упомянуть, что существует ограничение на количество w файловых дескрипторов sed , которые могут поддерживаться в одном скрипте. В спецификации указано :

[ sed требуется] для поддержки как минимум десяти отдельных файлов w , что соответствует исторической практике многих реализаций.Реализациям рекомендуется поддерживать больше, но соответствующие приложения не должны превышать этого предела.

Таким образом, описанная выше команда должна быть переносимой в любую систему POSIX для одновременного чтения / записи до 10 файлов. Если бы такие вещи были включены в опубликованный скрипт или приложение, в котором могло бы потребоваться больше, возможно, стоит выполнить несколько проверок перед обработкой реальных данных в / tmp . Например:

: & set '"" "" "" "" "" "" "" "" "" "" ';n='
' f=/tmp/$$$!'_$((i+=1))' MAXw=[num]
while eval "set '$1$1' $1;exec <<!$n\$(((i=0)+\$#))$n!$n 
      i=\$(sed \"$(IFS=\ ;printf "\nw $f%.0s" $1)\")"
      [ "$(($#==i?(_i=i-1):(MAXw=_i)))" -lt "$MAXw" ]
do :;done; rm "/tmp/$$$!"*; unset _i i f n

... который должен достаточно портативно оценить возможности sed в этой области. GNU sed застопорился на 4093 одновременно открытых для меня w файлах примерно за секунду, но это, вероятно, максимум моей системы, и на него может повлиять ulimit также. Когда это было выполнено - поскольку проверка удваивает значение $ i для каждой попытки - $ _ i остается на уровне 2560, а $ i - на 5120. Я по умолчанию для установки $ MAXw более безопасного $ _ i выше при закрытии цикла - в основном потому, что я не уверен, все ли sed s будут правильно устанавливать свой возврат, если они не могут откройте файл w - но читатель может делать с ним все, что захочет.

Обратите внимание, что начальное значение [num] для $ MAXw должно быть фактическим числом - каким бы ни был ваш максимальный размер файлов w , а не буквально [число] .

Еще раз о документе здесь - я считаю это - или что-то в этом роде - хорошей идеей в данном случае. sed должен поддерживать свои дескрипторы записи во время чтения, поэтому я не знаю, что он может делать с идентичными входными / исходящими именами, но я не думаю, что стоит рисковать, когда альтернативы так легко доступны нам.

Мои тестовые файлы были сгенерированы вроде:

for n in 1 2 3 4 5
do : & seq -s "$(printf "%015s--$n--%015s\n\t")" "$!" >"file$n"
done

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

До:

for f in file[1-5]; do
nl -ba "$f" | sed -n '$p;$=;1,3p
'; done

     1  1               --1--             
     2          2               --1--     
     3          3               --1--     
  3681          3681               --1--  
3681
     1  1               --2--             
     2          2               --2--     
     3          3               --2--     
  3683          3683               --2--  
3683
     1  1               --3--             
     2          2               --3--     
     3          3               --3--     
  3684          3684               --3--  
3684
     1  1               --4--             
     2          2               --4--     
     3          3               --4--     
  3684          3684               --4--  
3684
     1  1               --5--             
     2          2               --5--     
     3          3               --5--     
  3685          3685               --5--  
3685

Если форматирование выглядит немного странно, это, вероятно, связано с тем, что seq не вставляет -s строка-разделитель перед первой строкой вывода. Важно то, что sed , seq и nl , похоже, совпадают по номерам строк. В любом случае ...

После: ...

  sed -n 
  1,737w ./file1

  3682,4418w ./file2

  7365,8101w ./file3

  11049,11785w ./file4

  14733,15470w ./file5
  ...
     1  1               --1--           
     2          2               --1--   
     3          3               --1--   
   737          737               --1-- 
737
     1  1               --2--           
     2          2               --2--   
     3          3               --2--   
   737          737               --2-- 
737
     1  1               --3--           
     2          2               --3--   
     3          3               --3--   
   737          737               --3-- 
737
     1  1               --4--           
     2          2               --4--   
     3          3               --4--   
   737          737               --4-- 
737
     1  1               --5--           
     2          2               --5--   
     3          3               --5--   
   738          738               --5-- 
738

И вот оно - просто, эффективно и потоковое.

5
27.01.2020, 20:46

Теги

Похожие вопросы