@ wjandrea уже объяснил, как это сделать;Я попытаюсь объяснить, почему оригинал не работал. Я также проиллюстрирую важную технику отладки для таких вещей: если сложный процесс не выполняет то, что вы хотите, разберите его на части и запустите отдельные части, и посмотрите, что они делают.
Вв этом случае первая итерация цикла работает так, как вы ожидаете (она печатает «User: art101c40»), поэтому давайте не будем об этом беспокоиться. Давайте посмотрим на вторую итерацию, где i
= 2. Подставляя это значение, он запускает команду:
echo User:$(tail -n 2 /etc/passwd | cut --delimiter=":" --fields="1")
Итак, давайте пробежимся по частям и посмотрим, что они делают. Во-первых, он запускает tail -n 2 /etc/passwd
, который напечатает последние две строки / etc / passwd. Примерно так:
art101c39:*:10139:99:Art 101 class 39:/home/art101c39:/bin/bash
art101c40:*:10140:99:Art 101 class 40:/home/art101c39:/bin/bash
Обратите внимание, что он печатает две последние строки (это то, что делает tail -n 2
), поэтому он печатает как art101c39, так и art101c40. В этом корень всей проблемы.
Но давайте продолжим отслеживать ее поведение. Этот вывод передается по каналу cut --delimiter=":" --fields="1"
, который печатает:
art101c39
art101c40
, который захватывается $( )
для использования в качестве аргументов echo
. Каждое из имен пользователей находится в отдельной строке, поэтому они разделены новой строкой, но $( )
не в двойных кавычках, поэтому он может быть разбит на слова. Разделение слов разбивает его на «слова» на основе пробелов (обычно пробелы, табуляции и новые строки), поэтому каждое имя пользователя просто становится отдельным словом. По сути, это становится "art101c39
" и "art101c40
". Если у любого из них были какие-либо подстановочные знаки в имени файла, оболочка попытается превратить их в списки соответствующих файлов;но здесь нет подстановочных знаков, поэтому этого не происходит. Эти "слова" затем добавляются в аргументы для echo
, давая:
echo User:art101c39 art101c40
echo
получает два аргумента: "User:art101c39
" и "art101c40
". Как всегда, он склеивает их вместе с пробелом между ними и печатает результат.
Подобные вещи происходят на третьей и четвертой итерациях, при tail
печатаются три, а затем четыре счета и все этиучетные записи, проходящие через остальную часть обработки.
Это можно исправить напрямую, добавив head
в конвейер: tail -n $i /etc/passwd | head -n 1 | ...
. При этом tail
напечатает последние $i
строки, а head
удалит все, кроме первой из них ($i
th от конца файла). Но этот подход имеет тенденцию быть неэффективным и (IMO) клугым. Это предполагает чтение всего файла, чтобы выделить одно поле из одной строки. Для четырех строк (учетных записей) вам необходимо прочитать весь файл четыре раза. Если вы собираетесь выбрать по два поля у каждого из этих пользователей, это будет означать чтение файла восемь раз.
Обычно есть более простой и лучший способ сделать это, но что это зависит от того, чтофактическая цель. Как правило, вы запускаете tail
один раз, а затем распространенные способы работы с этим выводом включают: awk -F:
делать всю работу;трубопровод к петле while IFS=: read -r user pw uid gid fullname homedir shell; do
;использование readarray -t
для вывода строк в массив оболочки, где вы можете работать с ними напрямую;и т.д.