Необходимо будет сохранить содержание файла так или иначе. Можно использовать переменную.
content=`cat`
x=`echo "$content" | grep -o '>' | wc -l`
y=`echo "$content" | grep -o '<' | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Или временный файл (необходимый, если example.html
содержит пустые байты).
tmp=`mktemp`
trap "rm $tmp" EXIT
x=`grep -o '>' "$tmp" | wc -l`
y=`grep -o '<' "$tmp" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Если чтение содержания файла от stdin не является требованием, можно передать имя файла как аргумент сценарию.
x=`grep -o '>' "$1" | wc -l`
y=`grep -o '<' "$1" | wc -l`
if [ "$x" -ne "$y" ]; then
echo "Mismatch!"
fi
echo $x
echo $y
Назовите сценарий как так:
$ ./myscript.sh example.html
Что касается расширения глобуса, возможно превышающего предел - да и нет. Оболочка уже запущена, поэтому не остановится. Но если бы вы передали весь глобальный массив в качестве аргументов одной команде, тогда да, это вполне возможно. Переносимый и надежный способ справиться с этим включает в себя find
...
find . \! -name . -prune -name pattern -type f -exec cat {} + | ...
... который будет только cat
обычных файлов в текущем каталоге с именем, которое соответствует шаблону
, но также вызовет cat
столько раз, сколько необходимо, чтобы избежать превышения ARG_MAX
.
На самом деле, поскольку у вас есть GNU sed
, мы можем почти сделать все с помощью sed
в find
] скрипт.
cd /path/to/xmls
find . \! -name . -prune -name \*.xml -type f -exec \
sed -sne'1F;$x;/\n*\( \)*<\/*double>/!d' \
-e '$s//\1/gp;H' {} + | paste -d\\0 - -
Я придумал другой способ. Это будет очень быстро, но это абсолютно зависит от того, что в файле должно быть ровно 168 совпадений, а может быть только одно .
точка в именах файлов.
( export LC_ALL=C; set '' - -
while [ "$#" -lt 168 ]; do set "$@$@"; done
shift "$((${#}-168))"
find . \! -name . -prune -name \*.xml -type f \
-exec grep -F '<double>' /dev/null {} + |
tr \<: '>>' | cut -d\> -f1,4 | paste -d\ "$@" |
sed 'h;s|./[^>]*>||g;x;s|\.x.*||;s|..||;G;s|\n| |'
)
В соответствии с просьбой, вот небольшая разбивка того, как работает эта команда:
(...)
экспорт LC_ALL = C; set '' - -
C
, мы можем сэкономить нашим фильтрам много усилий.В локали UTF-8 любой символ может быть представлен одним или несколькими байтами за кусок, и любой найденный символ должен быть выбран из группы, состоящей из многих тысяч возможных. В локали C каждый символ представляет собой один байт, а их всего 128. Это значительно ускоряет поиск символов. set
изменяет позиционные параметры оболочки. Выполнение набора '' - -
устанавливает $ 1
в \ 0
, а $ 2
и $ 3
в -
. while ... set "$ @ $ @"; Выполнено; shift ...
paste
, чтобы заменить последовательные наборы из 167 символов новой строки пробелами, сохранив при этом 168-е. Самый простой способ сделать это - дать ему 168 ссылок на аргументы на -
stdin и указать, чтобы он склеил их все вместе. find ... -exec grep -F '' / dev / null '...
find
уже обсуждался ранее, но с grep
мы печатаем только те строки, которые могут быть сопоставлены с -F
фиксированной строкой
. Сделав первый аргумент grep
/ dev / null
- файл, который никогда не может соответствовать нашей строке - мы гарантируем, что grep
] всегда ищет 2 или более аргумента файла для каждого вызова. При вызове с двумя или более именованными файлами поиска grep
всегда будет печатать имя файла, например file_000.xml:
в начале каждой выходной строки. tr \ <: '>>'
grep
символов :
или <
в >
. ./ file_000.xml>> double> 0,0000> / double>
. cut -d \> -f1,4
cut
удалит из своего вывода все входные данные, которые не могут быть найдены ни в 1-м, ни в 4-м полях, разделенных на >
символов. . ./ file_000.xml> 0,0000
. paste -d \ "$ @"
вставляем
строки ввода группами по 168. ./file_000.xml>0.000 ... / file_000.xml> 0.167
sed 'h; s | ./ [^>] *> || g; x; s | \ .xml. * ||; s | .. ||; G; s | \ n | | '
cut
и paste
намного быстрее, чем любые попытки эмуляции, которые мы могли бы сделать с помощью утилит более высокого уровня, таких как sed
] или, что еще хуже, awk
. Но я зашел настолько далеко, насколько мог себе представить, что смогу зайти так далеко, и мне нужно вызвать sed
. h
старую копию каждой входной строки, затем я g
локально удаляю все вхождения шаблона ./ [^>] *>
в пространстве шаблонов - так что каждое вхождение имени файла. На данный момент пространство шаблонов sed
выглядит так: 0,000 0,0001 ... 0,167
x
change h
old and pattern пробелы и удалите все из \. xml. *
на -так что все, начиная с первого имени файла в сохраненной копии строки дальше. Затем я удаляю первые два символа - или ./
также - и в этот момент пространство шаблонов выглядит как file_000
. G
и копия старого пространства h
, добавленная к пространству шаблонов после символа \ n
ewline char, затем I s ///
ubставьте строку \ n
вместо пробела. file_000 0,000 ... 0,167
. И это то, что sed
записывает в вывод для каждого файла, который find
передает в grep
. Посмотрите на содержимое/etc/nsswitch.conf. Возможно, система не настроена на использование DNS для разрешения имен узлов. nslookup и dig не удосуживаются посмотреть, настроена ли система на использование DNS для разрешения имен хостов. Они используют DNS независимо. (Хотя вы не указываете сервер, они будут использовать/etc/resolv.conf, чтобы найти DNS сервер для использования.)
Вы хотите видеть DNS в строке хостов, что-то вроде hosts: files dns
Вы пишете дважды для каждого файла. Это, вероятно, самая дорогая вечеринка. Вместо этого вы хотите попытаться сохранить все это в памяти, вероятно, в массиве. Тогда напишите один раз в конце концов.
Загляните в ulimit
, если вы начнете достигать пределов памяти. Если вы увеличиваете эту рабочую нагрузку до 10-100 раз, вы смотрите на 10-100 ГБ памяти. Вы можете пакетировать это в цикл, который делает так много тысяч за итерацию. Я не уверен, что это должен быть повторяемый процесс, но стать более сложным, если вам нужно, чтобы он был быстрее/надежнее. В противном случае, сшить партии вручную после этого.
Вы также порождаете несколько процессов на файл - каждый канал, который у вас есть. Можно выполнить весь анализ/munging (grep/sed/tr) с помощью одного процесса. После этого Zsh может обрабатывать другие переводы с помощью расширений (см. man zshexpn
). Также можно выполнить все одиночные строки sed
в одном вызове с несколькими выражениями. sed
может быть быстрее, если вы избегаете -r
(расширенный регекс) и жадности. Ваш grep
может просто вытащить соответствующие строки из нескольких файлов одновременно и записать в промежуточные временные файлы. Знайте свои узкие места и не устраняйте их.
Для каждого файла можно использовать одну вставку. Множественные разделители awk выполняют эффективное разделение и tr объединяет все строки в памяти, а не на диске.
for f in `ls *.xml` ;
do
echo $f,`grep double $f | awk -F '[<>]' '{print $3}' | tr '\n' ','`;
done
Я не могу профилировать это в своем конце - так как у меня нет тех же данных, но я думаю, что это должно быть быстрее.
Кроме того, это самая простая проблема для разделения и правила - если у вас есть доступ к нескольким машинам или фермам, вы можете просто разделить всю задачу на несколько машин и, наконец, объединить все выходы в один файл. Таким образом, можно управлять ограничениями командной строки и памятью.
Скорее всего, по историческим причинам и/или по обратной совместимости.
Это часть пакета основных коммунальных услуг GNU, поэтому он будет рядом, пока Ричард Столлман и другие не почувствуют, что необходимо очистить его от существования.
-121--64144-Посмотрите на содержимое/etc/nsswitch.conf. Возможно, система не настроена на использование DNS для разрешения имен узлов. nslookup и dig не удосуживаются посмотреть, настроена ли система на использование DNS для разрешения имен хостов. Они используют DNS независимо. (Хотя вы не указываете сервер, они будут использовать/etc/resolv.conf, чтобы найти DNS сервер для использования.)
Вы хотите видеть DNS в строке хостов, что-то вроде hosts: files dns
Вы пишете дважды для каждого файла. Это, вероятно, самая дорогая вечеринка. Вместо этого вы хотите попытаться сохранить все это в памяти, вероятно, в массиве. Тогда напишите один раз в конце концов.
Загляните в ulimit
, если вы начнете достигать пределов памяти. Если вы увеличиваете эту рабочую нагрузку до 10-100 раз, вы смотрите на 10-100 ГБ памяти. Вы можете пакетировать это в цикл, который делает так много тысяч за итерацию. Я не уверен, что это должен быть повторяемый процесс, но стать более сложным, если вам нужно, чтобы он был быстрее/надежнее. В противном случае, сшить партии вручную после этого.
Вы также порождаете несколько процессов на файл - каждый канал, который у вас есть. Можно выполнить весь анализ/munging (grep/sed/tr) с помощью одного процесса. После этого Zsh может обрабатывать другие переводы через расширения (см. man zshexpn
). Также можно выполнить все одиночные строки sed
в одном вызове с несколькими выражениями. sed
может быть быстрее, если вы избегаете -r
(расширенный регекс) и жадности. Ваш grep
может просто вытащить соответствующие строки из нескольких файлов одновременно и записать в промежуточные временные файлы. Знайте свои узкие места и не устраняйте их.
Это должно помочь:
awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' $path_to_xml/*.xml > final_table.csv
awk
: используйте программу awk
, я тестировал ее с GNU awk 4.0.1-F '[<>]"
: используйте <
и >
в качестве разделителей полейNR! =1 && FNR==1{printf "\n"}
: если это не первая строка в целом (NR!=1
), а первая строка файла (FNR==1
), то выведите новую строкуFNR==1{sub(".*/", "", FILENAME); sub(". xml$", "", FILENAME); printf FILENAME}
: если это первая строка файла, удалите что-либо до последней /
(sub(".*/", "", FILENAME)
) в имени файла (FILENAME
), удалите трейлинг . xml
(sub(". xml$", "", FILENAME)
) и выведите результат (printf FILENAME
)/double/{printf " %s", $3}
, если строка содержит "double" (/double/
), выведите пробел, за которым следует третье поле (printf " %s", $3
). Используя <
и >
в качестве разделителей, это будет число (при этом первым полем будет что угодно до первого <
, а вторым - double
). При желании вы можете отформатировать числа здесь. Например, используя %8.3f
вместо %s
, будет выведено любое число с 3-мя знаками после запятой и общей длиной (включая точку и знак после запятой) не менее 8. $path_to_xml/*.xml
: список файлов> final_table.csv
: поместите результат в final_table. csv
, перенаправляя выводВ случае ошибок "список аргументов на длинный", можно использовать find
с параметром -exec
для генерации списка файлов, вместо того, чтобы передавать его напрямую:
find $path_to_xml -maxdepth 1 -type f -name '*.xml' -exec awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' {} + > final_table.csv
find $path_to_xml
: скажите find
to list files in $path_to_xml
-maxdepth 1
: не опускайтесь в подпапки $path_to_xml
-типа f
: перечислять только обычные файлы (это также исключает сам $path_to_xml
)-имя '*. xml': только файлы списка, которые соответствуют шаблону
*.xml`, это должно быть процитировано, иначе оболочка попытается расширить шаблон-exec COMMAND {}. +
: выполните команду COMMAND
с соответствующими файлами в качестве параметров вместо {}
. +
указывает на то, что могут передаваться сразу несколько файлов, что уменьшает форкировку. Если вы используете \;
(;
необходимо процитировать, иначе это интерпретируется оболочкой) вместо +
, команда выполняется для каждого файла в отдельности. Также можно использовать xargs
вместе с find
:
find $path_to_xml -maxdepth 1 -type f -name '*.xml' -print0 |
xargs -0 awk -F '[<>]' '
NR!=1 && FNR==1{printf "\n"}
FNR==1{sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
END{printf "\n"}
' > final_table.csv
-print0
: выводится список файлов, разделенных нулевыми символами|
(pipe): перенаправляет стандартный вывод find
на стандартный вход xargs
xargs
: строит и выполняет команды со стандартного входа, i. e. запускает команду для каждого переданного аргумента (здесь имена файлов). -0
: направляет xargs
на предположение, что аргументы разделены нулевыми символамиawk -F '[<>]' '
BEGINFILE {sub(".*/", "", FILENAME); sub(".xml$", "", FILENAME); printf FILENAME}
/double/{printf " %s", $3}
ENDFILE {printf "\n"}
' $path_to_xml/*.xml > final_table.csv
, где BEGINFILE
, ENDFILE
вызывается при смене файла (если ваш awk поддерживает это).
Пожалуйста, от имени будущих программистов и системных администраторов - НЕ используйте регулярное выражение для синтаксического анализа XML. XML - это структурированный тип данных, и он НЕ подходит для синтаксического анализа регулярных выражений - вы можете «подделать его», представив его обычным текстом, но в XML есть множество семантически идентичных вещей, которые не анализируют то же самое. Вы можете встроить перевод строки и, например, иметь унарные теги.
Таким образом - используйте парсер - я смоделировал некоторые исходные данные, потому что ваш XML недействителен. Дайте мне более полный образец, и я дам вам более полный ответ.
На базовом уровне - мы извлекаем узлы double
следующим образом:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new;
$twig -> parse ( \*DATA );
foreach my $double ( $twig -> get_xpath('//double') ) {
print $double -> trimmed_text,"\n";
}
__DATA__
<root>
<subnode>
<another_node>
<double>1.2342</double>
<double>2.3456</double>
<some_other_tag>fish</some_other_tag>
</another_node>
</subnode>
</root>
Это печатает:
1.2342
2.3456
Итак, мы расширяем это:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use Text::CSV;
my $twig = XML::Twig->new;
my $csv = Text::CSV->new;
#open our results file
open( my $output, ">", "results.csv" ) or die $!;
#iterate each XML File.
foreach my $filename ( glob("/path/to/xml/*.xml") ) {
#parse it
$twig->parsefile($filename);
#extract all the text of all the 'double' elements.
my @doubles = map { $_->trimmed_text } $twig->get_xpath('//double');
#print it as comma separated.
$csv->print( $output, [ $filename, @doubles ] );
}
close($output);
Я думаю, что это должно помочь (без образцов данных , Точно не могу сказать). Но обратите внимание - используя синтаксический анализатор XML, мы не запутаемся с некоторым переформатированием XML, которое может быть выполнено совершенно корректно (как в соответствии со спецификацией XML). Используя синтаксический анализатор CSV, мы не собираемся попасть в ловушку каких-либо полей со встроенными запятыми или перевода строки.
Если вы ищете более конкретные узлы - вы можете указать более подробный путь. Как бы то ни было, приведенное выше просто ищет любой экземпляр double
. Но вы можете использовать:
get_xpath("/root/subnode/another_node/double")