Это не слишком редко, чтобы иметь инструменты, которые ожидают быть установленными на уровне пользователя. По сути, они не предположат, что можно изменить что-либо непосредственно под /usr
. Часто распространено, однако, иметь a ~/bin
или ~/usr/bin
каталог, где можно включать символьные ссылки на инструменты, которые Вы установили для своего пользователя. Таким образом, что Вы не должны постоянно обновлять a $PATH
переменная.
Таким образом, создавая один пример для работы из:
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% строк, мы можем заменить stat --printf %s "$f"
на:
wc -l < "$f"
Так как мы используем printf
и bc
, мы можем эффективно округлить от . 5
, однако, если файл длиной всего 1 или 2 строки, он их пропустит. Поэтому мы бы хотели не только округлить, но и по умолчанию, по крайней мере, захватить 1 строку.
Использование упомянутых вами инструментов + Найти
:
Получите процент строк или байт 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
Черт. Я написал весь этот большой ответ с помощью тщательно разработанного метода синтаксического анализа архива 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
И вот оно - просто, эффективно и потоковое.