Математические операции над массивами из зарегистрированных строк

- бесплатный, современный и кросс-платформенный редактор LaTeX для систем linux, macosx и windows, который объединяет множество инструментов, необходимых для разработки документов с помощью LaTeX, всего в одном приложении .

Texmaker включает поддержку Unicode, проверку орфографии, автозаполнение, сворачивание кода и встроенный просмотрщик PDF с поддержкой Synctex и непрерывным режимом просмотра. Texmaker прост в использовании и настраивается.

Texmaker выпущен под лицензией GPL.

2
задан 17 September 2017 в 21:33

2 ответа

Я взял на себя смелость немного изменить вашу функцию и поместить все в простой скрипт. Ядро проблемы заключается в том, что вам нужно было эхо после завершения цикла while. Кроме того, функции bash не возвращают массивы, вам нужно как-то отбросить их в stdout или использовать основную функцию и иметь основной массив local, который затем может быть доступен для дочерних функций (это то, что я делаю довольно часто в моих собственных скриптах).

Вот результат теста. Для 9 итераций, где столбец 1 всегда равен 110, мы получаем 990.

$ ./generate_lines.sh 990 1008 1035 1008 1017 1080

И вот сценарий:

#!/usr/bin/env bash sum_line_tokens() { while read line do #echo "$line" logLine=$line # saves currently logged line in variable logLine # redirect variable logLine as input for read command. # read -a saves word of input string as array. InternalFieldSeparator set as ';' # detects elements in input string which are separated by '; ' as words. IFS=';' read -a arrayLog <<< $logLine for n in 1 3 5 7 9 11 do # define element in arraySum at position n as sum of previous element # and element in arrayLog at this position arraySum[n]=$(( ${arraySum[n]} + ${arrayLog[n]} )) #echo "${arraySum[n]}" done done # Functions in bash can only use return to indicate exit status # This is more like int datatype for C or Java functions. If you want # to return a string or array, you need to echo it to stdout echo "${arraySum[@]}" } generate_lines(){ while [[ $i < 9 ]] do i=$(($i + 1)) echo "dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935" done } generate_lines | sum_line_tokens

Упрощение задачи с помощью awk

Пока скрипт работает, он длинный. Мы можем сократить решение с помощью awk:

# again, same thing - the script now generates lines only, no summing. # We'll pipe it to awk $ ./generate_lines.sh dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 $ ./generate_lines.sh | awk -F ';' '{for(i=1;i<=11;i++) if(i%2 != 0) sum[i+1]+=$(i+1) }END{for(j in sum) printf "%d\t",sum[j];print ""}' 990 1008 1035 1008 1017 1080

Использование основной функции и локального массива

Я большой сторонник использования функций main в скриптах, потому что он держит все и в качестве дополнительного преимущества вы можете объявить локальную переменную, которую каждая функция, которую вы вызываете из main, будет знать и сможет перезаписать.

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

Поэтому нам нужна нейтральная земля - ​​либо мы используем временный файл, либо что-то известное как «named pipe». В этом примере я просто использовал временный файл. Если вещь, которую нужно разобрать, не слишком велика, вы всегда можете хранить все в локальной переменной и позволить двум функциям иметь дело с той же самой переменной - одна запись в переменную, одна - разбор переменной. В случае длинного текста, который попадает в тысячи строк, лучше использовать некоторый временный файл.

Итак, в этой версии скрипта я рассмотрел несколько вещей, включая основную функцию и способ получения главной функции аргументы командной строки, а также то, что вы запросили в комментариях. В принципе, скрипт теперь получает 1 аргумент командной строки - это число строк, которое вы хотите, и даете это функции sum_line_tokens. Без аргументов командной строки он суммирует все строки.

Тестирование:

$ ./generate_lines.sh 3 330 336 345 336 339 360 $ ./generate_lines.sh 4 440 448 460 448 452 480

И сам скрипт:

#!/usr/bin/env bash sum_line_tokens() { # To perform counting for n number of lines, use a counter variable # In this case I am using argument passed from command-line linecount=0 # IFS= and -r for better line reading to ensure that spaces won't mess you up while IFS='' read -r line do # Check if we have arg 1 to function and quit after n lines if [ -n $1 ] && [ $linecount -eq $1 ] then break fi logLine=$line IFS=';' read -a arrayLog <<< $logLine for n in 1 3 5 7 9 11 do arraySum[n]=$(( ${arraySum[n]} + ${arrayLog[n]} )) done # increment line counter ((linecount++)) done } generate_lines(){ while [[ $i < 9 ]] do i=$(($i + 1)) echo "dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935" done } main(){ # create local array. Any function called from main will know about it local -a arraySum # We can't just pipe lines to summing function. Whatever runs on the right-hand side # of the pipe runs in subshell, which means when that subshell exits, your variables are gone # See https://askubuntu.com/q/704154/295286 tempfile=$(mktemp) generate_lines > "$tempfile" sum_line_tokens "$1" < "$tempfile" echo "${arraySum[@]}" rm "$tempfile" } # Call main function with the command-line arguments. This works sort of like int main(String[] args) in Java main "$@"

Обратите внимание на переносимость

Конечно, потому что мы используем много особых вещей, связанных с bash, если вы запускаете это в системе, где bash недоступен, это не сработает. Это хороший сценарий? Да, это делает работу. Этот переносимый сценарий? Нет. Решение awk выше, вероятно, более портативное.

2
ответ дан 18 July 2018 в 06:40

Я взял на себя смелость немного изменить вашу функцию и поместить все в простой скрипт. Ядро проблемы заключается в том, что вам нужно было эхо после завершения цикла while. Кроме того, функции bash не возвращают массивы, вам нужно как-то отбросить их в stdout или использовать основную функцию и иметь основной массив local, который затем может быть доступен для дочерних функций (это то, что я делаю довольно часто в моих собственных скриптах).

Вот результат теста. Для 9 итераций, где столбец 1 всегда равен 110, мы получаем 990.

$ ./generate_lines.sh 990 1008 1035 1008 1017 1080

И вот сценарий:

#!/usr/bin/env bash sum_line_tokens() { while read line do #echo "$line" logLine=$line # saves currently logged line in variable logLine # redirect variable logLine as input for read command. # read -a saves word of input string as array. InternalFieldSeparator set as ';' # detects elements in input string which are separated by '; ' as words. IFS=';' read -a arrayLog <<< $logLine for n in 1 3 5 7 9 11 do # define element in arraySum at position n as sum of previous element # and element in arrayLog at this position arraySum[n]=$(( ${arraySum[n]} + ${arrayLog[n]} )) #echo "${arraySum[n]}" done done # Functions in bash can only use return to indicate exit status # This is more like int datatype for C or Java functions. If you want # to return a string or array, you need to echo it to stdout echo "${arraySum[@]}" } generate_lines(){ while [[ $i < 9 ]] do i=$(($i + 1)) echo "dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935" done } generate_lines | sum_line_tokens

Упрощение задачи с помощью awk

Пока скрипт работает, он длинный. Мы можем сократить решение с помощью awk:

# again, same thing - the script now generates lines only, no summing. # We'll pipe it to awk $ ./generate_lines.sh dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935 $ ./generate_lines.sh | awk -F ';' '{for(i=1;i<=11;i++) if(i%2 != 0) sum[i+1]+=$(i+1) }END{for(j in sum) printf "%d\t",sum[j];print ""}' 990 1008 1035 1008 1017 1080

Использование основной функции и локального массива

Я большой сторонник использования функций main в скриптах, потому что он держит все и в качестве дополнительного преимущества вы можете объявить локальную переменную, которую каждая функция, которую вы вызываете из main, будет знать и сможет перезаписать.

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

Поэтому нам нужна нейтральная земля - ​​либо мы используем временный файл, либо что-то известное как «named pipe». В этом примере я просто использовал временный файл. Если вещь, которую нужно разобрать, не слишком велика, вы всегда можете хранить все в локальной переменной и позволить двум функциям иметь дело с той же самой переменной - одна запись в переменную, одна - разбор переменной. В случае длинного текста, который попадает в тысячи строк, лучше использовать некоторый временный файл.

Итак, в этой версии скрипта я рассмотрел несколько вещей, включая основную функцию и способ получения главной функции аргументы командной строки, а также то, что вы запросили в комментариях. В принципе, скрипт теперь получает 1 аргумент командной строки - это число строк, которое вы хотите, и даете это функции sum_line_tokens. Без аргументов командной строки он суммирует все строки.

Тестирование:

$ ./generate_lines.sh 3 330 336 345 336 339 360 $ ./generate_lines.sh 4 440 448 460 448 452 480

И сам скрипт:

#!/usr/bin/env bash sum_line_tokens() { # To perform counting for n number of lines, use a counter variable # In this case I am using argument passed from command-line linecount=0 # IFS= and -r for better line reading to ensure that spaces won't mess you up while IFS='' read -r line do # Check if we have arg 1 to function and quit after n lines if [ -n $1 ] && [ $linecount -eq $1 ] then break fi logLine=$line IFS=';' read -a arrayLog <<< $logLine for n in 1 3 5 7 9 11 do arraySum[n]=$(( ${arraySum[n]} + ${arrayLog[n]} )) done # increment line counter ((linecount++)) done } generate_lines(){ while [[ $i < 9 ]] do i=$(($i + 1)) echo "dateTime;110;2930;112;2931;115;2932;112;2933;113;2934;120;2935" done } main(){ # create local array. Any function called from main will know about it local -a arraySum # We can't just pipe lines to summing function. Whatever runs on the right-hand side # of the pipe runs in subshell, which means when that subshell exits, your variables are gone # See https://askubuntu.com/q/704154/295286 tempfile=$(mktemp) generate_lines > "$tempfile" sum_line_tokens "$1" < "$tempfile" echo "${arraySum[@]}" rm "$tempfile" } # Call main function with the command-line arguments. This works sort of like int main(String[] args) in Java main "$@"

Обратите внимание на переносимость

Конечно, потому что мы используем много особых вещей, связанных с bash, если вы запускаете это в системе, где bash недоступен, это не сработает. Это хороший сценарий? Да, это делает работу. Этот переносимый сценарий? Нет. Решение awk выше, вероятно, более портативное.

2
ответ дан 24 July 2018 в 18:39
  • 1
    Большое вам спасибо @Sergiy Kolodyazhnyy, насколько я понял ваш ответ, мне пришлось бы вывести выходную строку функции sum_line_tokens в другой скрипт, который перед выполнением другой операции снова преобразует ее в массив. Не могли бы вы объяснить, что ваш упомянутый подход вместо «локального массива в основном, который затем может быть доступен для дочерних функций»? – brunuser 17 September 2017 в 22:57
  • 2
    @brunuser уверен, я могу добавить это также. Я также работаю над добавлением версии awk в решение. – Sergiy Kolodyazhnyy 17 September 2017 в 23:17
  • 3
    если я могу задать другой вопрос: знаете ли вы об изящном способе выполнения функции sum_line_tokens только для n строк , независимо от того, сколько еще строк (т. е. бесконечных в случае ttylog) генерируются функцией generate_lines ? – brunuser 17 September 2017 в 23:36
  • 4
    @brunuser сделано. Я также обратился к этому комментарию – Sergiy Kolodyazhnyy 18 September 2017 в 00:15
  • 5
    Средняя часть этого ответа о трубах и подоболочках - это основная проблема, даже для оригинального сценария OP. Использование альтернативы каналу, который не запускает linesToArraySum / sum_line_tokens в подоболочке, является полным исправлением, даже без , другие изменения, такие как предварительное объявление arraySum и использование main. Исходный код OP будет работать , если вместо перехода от echo используется строка здесь - или что-либо, что не запускает функцию в подоболочке. С учетом сказанного, я полностью согласен с советом использовать awk для этого! – Eliah Kagan 18 September 2017 в 03:16

Другие вопросы по тегам:

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