Захват содержимого цикла в переменную

У меня есть цикл for, который выводит все из /etc/trueuserowners в du. Я пытаюсь выяснить, как получить выход этого цикла for и поместить его в переменную без привязки к массиву.

for i in $(grep dook /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr

Основной целью этого является то, что я могу взять вывод du -sk, поместить его в переменную p и взять p, чтобы вычислить человеческие читаемые формы и получить общий в самом конце.

Я пытаюсь удержать это в одном лайнере, но могу / будет преобразовывать в bash-скрипт, если я НЕОБХОДИМО выполнить.

Это выполняется на сервере с пользователями cPanel.

Содержимое /etc/trueuserowners выглядит следующим образом.

user1: reseller user2: reseller user3: reseller

Это будет выход пример из того, что я использую, будет выглядеть следующим образом.

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr 992K /home/demoabusecenter 820K /home/lakshdljkashdlkj 151M /home/hyg8 58M /home/bugsabusecenter 21G /home/esportsoverlay 3.3G /home/yourabuse 2.8M /home/kf30ls08f2k0 1.4M /home/perm2term 1.2M /home/justcheck 1.1M /home/myinfo1

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

Я думаю, у меня есть решение для этого, но я не могу понять, как получить этот вывод в переменной.

Это то, что происходит, когда я пытаюсь.

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do p=$(du -sh /home/$i); done | sort -nr | echo $p 20981044 /home/esportsoverlay

По какой-то причине он преобразует вывод для пробела в MB. Это нормально, потому что я могу преобразовать это в GB. Я просто не могу заставить его показать все. Это показывает только последний пользовательский вывод из цикла for.

2
задан 2 December 2017 в 02:49

3 ответа

Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.

Хорошо, давайте начнем с того, что ваш подход к итерации выход grep не совсем прав. Подход for i in $(); do ...done разбивается на строки, содержащие ведущие пробелы (из-за разбиения слов), и обычно не рекомендуется (см. Это).

Однако, что обычно делается, это command | while IFS=<word separator> read -r variable; do...done (который как дополнительный бонус переносится между борнеподобными снарядами). Кроме того, я вижу, что вы используете cut -d : -f 1, чтобы получить первый элемент из списка разделенных двоеточием в каждой строке. Мы можем использовать IFS=":", чтобы избавиться от части cut. Таким образом, ваша исходная команда преобразуется так:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else 
do 
    du -sh /home/$i
done | sort -nr

Переменная first_word, очевидно, будет содержать только первое поле, а неиспользуемый everything_else будет содержать ... все остальное.

Обратите внимание, что я также цитировал 'dook' часть; хотя grep понимает первый элемент. Хотя grep понимает первый элемент без опции как PATTERN, действительно рекомендуется защищать шаблон с одинарными кавычками, потому что если вы используете * или некоторые другие шаблоны регулярных выражений, которые используются оболочкой, это будет непреднамеренно выполнять имя файла расширение bash и попытается прочитать файлы в вашем текущем рабочем каталоге. См. разбиение слова для подробного примера.

Далее, давайте обратимся к массивам. Мы можем добавить новый элемент в массивы с помощью цикла while, показанного выше, и оператора +=:

$ declare -a my_array
$ while IFS=":" read -r name null; do my_array+=("$name"); done < /etc/passwd
$ echo "${my_array[0]}"
root

Это может быть удобно, если вы хотите обработать элементы, которые вы извлекаете позже , Однако, учитывая тот факт, что у вас есть труба, переменные исчезают после подоболочки, в которой while запускает выходы. См. Мой новый элемент массивов .

Я бы рекомендовал обрабатывать все в while loop и использовать du -sb для точности вычислений. Таким образом, вы можете сделать:

# read what grep finds line by line and print total once there's no more lines from grep
grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else || { echo "$total"; break;} 
do 
    user_usage=$( du -sb /home/"$first_word" | awk '{print $1}' )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$user_usage"  "$first_word"
    total=$(($total+$user_usage))
done 

Обратите внимание, как я использовал || { echo "$total"; break;}. После того, как читать stdin (что в этом случае происходит из трубы), команда read возвращает статус выхода 1, поэтому, когда read возвращает 1, мы знаем, что это сделано для чтения и обработки, и мы можем выводить итоговые данные используемое нами.

Что касается вывода данных, считываемых человеком, мы могли бы использовать numfmt или некоторые другие утилиты. Что-то вроде numfmt --to=iec-i --suffix=B $user_usage было бы достаточно.

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

Объединяя все это вместе, полное решение должно быть:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r username trash || { printf "%s\ttotal\n" $( numfmt --to=iec-i --suffix=B "$total"); break;} 
do 
    # get usage in bytes
    user_usage=$( du -sb /home/"$username" | awk '{print $1}' )
    # get human-readable
    usage_human_readable=$( numfmt --to=iec-i --suffix=B "$user_usage" )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$usage_human_readable"  "$username"
    total=$(($total+$user_usage))
done 
1
ответ дан 22 May 2018 в 15:44
  • 1
    Можете ли вы сказать мне, что такое numfmt? Я попытался получить man-страницу для него, и кажется, что команда не существует `man numfmt Нет ручного ввода для numfmt [root @ pro: / root] $ numfmt -bash: numfmt: command not found` – TheDocs 2 December 2017 в 03:10
  • 2
    @TheDocs numfmt - это команда, часть пакета coreutils, как и стандартные утилиты mv и cp. Проверьте версию пакета coreutils с помощью apt-cache show coreutils | grep 'Version'. Утилита numfmt существует с версии 8.21 (т. Е. С февраля 2013 года) – Sergiy Kolodyazhnyy 2 December 2017 в 03:13
  • 3
    Будет ли альтернатива использованию numfmt? Я бы не смог установить это на производственных серверах. Это будет работать в основном на серверах CentOS 6/7. Идея состоит в том, чтобы использовать эту помощь, чтобы найти перепроданных пользователей, перешедших на определенный порог. Кроме того, я должен поблагодарить вас за то, что показал мне другой способ использования цикла while. Я изучал это, и он сильно изменил мою перспективу и многому научился у него. Я очень ценю это. – TheDocs 4 December 2017 в 21:21

Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.

Хорошо, давайте начнем с того, что ваш подход к итерации выход grep не совсем прав. Подход for i in $(); do ...done разбивается на строки, содержащие ведущие пробелы (из-за разбиения слов), и обычно не рекомендуется (см. Это).

Однако, что обычно делается, это command | while IFS=<word separator> read -r variable; do...done (который как дополнительный бонус переносится между борнеподобными снарядами). Кроме того, я вижу, что вы используете cut -d : -f 1, чтобы получить первый элемент из списка разделенных двоеточием в каждой строке. Мы можем использовать IFS=":", чтобы избавиться от части cut. Таким образом, ваша исходная команда преобразуется так:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else do du -sh /home/$i done | sort -nr

Переменная first_word, очевидно, будет содержать только первое поле, а неиспользуемый everything_else будет содержать ... все остальное.

Обратите внимание, что я также цитировал 'dook' часть; хотя grep понимает первый элемент. Хотя grep понимает первый элемент без опции как PATTERN, действительно рекомендуется защищать шаблон с одинарными кавычками, потому что если вы используете * или некоторые другие шаблоны регулярных выражений, которые используются оболочкой, это будет непреднамеренно выполнять имя файла расширение bash и попытается прочитать файлы в вашем текущем рабочем каталоге. См. [D2] разбиение слова для подробного примера.

Далее, давайте обратимся к массивам. Мы можем добавить новый элемент в массивы с помощью цикла while, показанного выше, и оператора +=:

$ declare -a my_array $ while IFS=":" read -r name null; do my_array+=("$name"); done < /etc/passwd $ echo "${my_array[0]}" root

Это может быть удобно, если вы хотите обработать элементы, которые вы извлекаете позже , Однако, учитывая тот факт, что у вас есть труба, переменные исчезают после подоболочки, в которой while запускает выходы. См. Мой новый элемент массивов .

Я бы рекомендовал обрабатывать все в while loop и использовать du -sb для точности вычислений. Таким образом, вы можете сделать:

# read what grep finds line by line and print total once there's no more lines from grep grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else || { echo "$total"; break;} do user_usage=$( du -sb /home/"$first_word" | awk '{print $1}' ) # output the usage for current user printf "%s\t/home/%s\n" "$user_usage" "$first_word" total=$(($total+$user_usage)) done

Обратите внимание, как я использовал || { echo "$total"; break;}. После того, как читать stdin (что в этом случае происходит из трубы), команда read возвращает статус выхода 1, поэтому, когда read возвращает 1, мы знаем, что это сделано для чтения и обработки, и мы можем выводить итоговые данные используемое нами.

Что касается вывода данных, считываемых человеком, мы могли бы использовать numfmt или некоторые другие утилиты. Что-то вроде numfmt --to=iec-i --suffix=B $user_usage было бы достаточно.

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

Объединяя все это вместе, полное решение должно быть:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r username trash || { printf "%s\ttotal\n" $( numfmt --to=iec-i --suffix=B "$total"); break;} do # get usage in bytes user_usage=$( du -sb /home/"$username" | awk '{print $1}' ) # get human-readable usage_human_readable=$( numfmt --to=iec-i --suffix=B "$user_usage" ) # output the usage for current user printf "%s\t/home/%s\n" "$usage_human_readable" "$username" total=$(($total+$user_usage)) done
1
ответ дан 18 July 2018 в 02:04

Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.

Хорошо, давайте начнем с того, что ваш подход к итерации выход grep не совсем прав. Подход for i in $(); do ...done разбивается на строки, содержащие ведущие пробелы (из-за разбиения слов), и обычно не рекомендуется (см. Это).

Однако, что обычно делается, это command | while IFS=<word separator> read -r variable; do...done (который как дополнительный бонус переносится между борнеподобными снарядами). Кроме того, я вижу, что вы используете cut -d : -f 1, чтобы получить первый элемент из списка разделенных двоеточием в каждой строке. Мы можем использовать IFS=":", чтобы избавиться от части cut. Таким образом, ваша исходная команда преобразуется так:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else do du -sh /home/$i done | sort -nr

Переменная first_word, очевидно, будет содержать только первое поле, а неиспользуемый everything_else будет содержать ... все остальное.

Обратите внимание, что я также цитировал 'dook' часть; хотя grep понимает первый элемент. Хотя grep понимает первый элемент без опции как PATTERN, действительно рекомендуется защищать шаблон с одинарными кавычками, потому что если вы используете * или некоторые другие шаблоны регулярных выражений, которые используются оболочкой, это будет непреднамеренно выполнять имя файла расширение bash и попытается прочитать файлы в вашем текущем рабочем каталоге. См. [D2] разбиение слова для подробного примера.

Далее, давайте обратимся к массивам. Мы можем добавить новый элемент в массивы с помощью цикла while, показанного выше, и оператора +=:

$ declare -a my_array $ while IFS=":" read -r name null; do my_array+=("$name"); done < /etc/passwd $ echo "${my_array[0]}" root

Это может быть удобно, если вы хотите обработать элементы, которые вы извлекаете позже , Однако, учитывая тот факт, что у вас есть труба, переменные исчезают после подоболочки, в которой while запускает выходы. См. Мой новый элемент массивов .

Я бы рекомендовал обрабатывать все в while loop и использовать du -sb для точности вычислений. Таким образом, вы можете сделать:

# read what grep finds line by line and print total once there's no more lines from grep grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else || { echo "$total"; break;} do user_usage=$( du -sb /home/"$first_word" | awk '{print $1}' ) # output the usage for current user printf "%s\t/home/%s\n" "$user_usage" "$first_word" total=$(($total+$user_usage)) done

Обратите внимание, как я использовал || { echo "$total"; break;}. После того, как читать stdin (что в этом случае происходит из трубы), команда read возвращает статус выхода 1, поэтому, когда read возвращает 1, мы знаем, что это сделано для чтения и обработки, и мы можем выводить итоговые данные используемое нами.

Что касается вывода данных, считываемых человеком, мы могли бы использовать numfmt или некоторые другие утилиты. Что-то вроде numfmt --to=iec-i --suffix=B $user_usage было бы достаточно.

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

Объединяя все это вместе, полное решение должно быть:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r username trash || { printf "%s\ttotal\n" $( numfmt --to=iec-i --suffix=B "$total"); break;} do # get usage in bytes user_usage=$( du -sb /home/"$username" | awk '{print $1}' ) # get human-readable usage_human_readable=$( numfmt --to=iec-i --suffix=B "$user_usage" ) # output the usage for current user printf "%s\t/home/%s\n" "$usage_human_readable" "$username" total=$(($total+$user_usage)) done
1
ответ дан 24 July 2018 в 17:30

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

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