У меня есть цикл 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.
Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.
Хорошо, давайте начнем с того, что ваш подход к итерации выход 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
Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.
Хорошо, давайте начнем с того, что ваш подход к итерации выход 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
Ответ будет немного длинным, но я должен затронуть довольно много частей, поэтому придерживайтесь меня здесь.
Хорошо, давайте начнем с того, что ваш подход к итерации выход 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