2016-04-27 3 views
1

Я пытался понять использование IFS, но есть кое-что, о чем я не мог найти никакой информации.Что такое точное использование и значение «IFS =!»

Мой пример код:

#!/bin/sh 
# (C) 2016 Ergin Bilgin 

IFS=! 
for LINE in $(last -a | sed '$ d') 
do 
    echo $LINE | awk '{print $1}' 
done 
unset IFS 

Я использую этот код, чтобы напечатать последние пользователь построчно. Я полностью понимаю использование IFS, и в этом примере, когда я использую IFS по умолчанию, он читает слово за словом внутри моего цикла. И когда я использую IFS=!, он читается по строкам, как я хочу. Проблема здесь в том, что я ничего не мог найти об этом "!" в любом месте. Я не помню, откуда я это узнал. Когда я google о достижении такого же поведения, я вижу другие значения, которые обычно являются строками.

Итак, в чем смысл этого "!" и как это дает мне результат, который я желаю?

Благодаря

+1

Возможно, вы замените весь этот код на 'last -a | awk 'NF> 8 {print $ 1}' ' – John1024

ответ

1

Как вы знаете, если выход last был !, тем скрипт разделил бы строки ввода на этом символе.

Формат вывода last не стандартизирован (например, не в POSIX), но вы вряд ли найдете систему, в которой первый столбец содержит что-либо, кроме имени того, что инициировало действие. Например, я вижу это:

tom  pts/8  Wed Apr 27 04:25 still logged in michener.jexium-island.net 
tom  pts/0  Wed Apr 27 04:15 still logged in michener.jexium-island.net 
reboot system boot Wed Apr 27 04:02 - 04:35 (00:33)  3.2.0-4-amd64 
tom  pts/0  Tue Apr 26 16:23 - down (04:56)  michener.jexium-island.net 

продолжает

reboot system boot Fri Apr 1 15:54 - 19:03 (03:09)  3.2.0-4-amd64 
tom  pts/0  Fri Apr 1 04:34 - down (00:54)  michener.jexium-island.net 

wtmp begins Fri Apr 1 04:34:26 2016 

с Linux, а также различные дата-форматы, происхождения и т.д., на других машинах.

Установив IFS=!, сценарий устанавливает поле-разделитель к значению, которое вряд произойдет в выходе last, так что каждая строка считывается в LINE без разделения его. Обычно линии разбиваются на пробелы.

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

#!/bin/sh 

for LINE in $(last -a | sed -e '$ d' -e 's/ .*//') 
do  
    echo $LINE 
done 

который (начиная от примера в вопросе) достаточно, если количество логинов не является достаточно большим, чтобы превысить командную строку. Проверяя изменения в выходе last, я заметил одну машину с примерно 9800 линиями с нескольких лет. (Другие обычные мотивы, заданные для не использования for-loops, в этом случае неправдоподобны). В качестве трубы:

#!/bin/sh 

last -a | sed -e 's/ .*//' -e '/^$/d' | while IFS= read LINE 
do 
    echo $LINE 
done 

Я изменил патч в выражение (которое, вероятно, ОП, скопированный из некоторого места, такие как Bash - remove the last line from a file), потому что он не работает.

И, наконец, использование опции -alast не требуется, поскольку вся дополнительная информация, которую он предоставляет, отбрасывается.

+0

[Почему вы не читаете строки с« для »] (http://mywiki.wooledge.org/DontReadLinesWithFor) и [BashFAQ/001] (http: // mywiki .wooledge.org/BashFAQ/001) – anubhava

2

IFS=! это установка несуществующие значения для IFS, так что вы можете перебирать входную строку. Сказав, что, используя цикл здесь не рекомендуется, лучше использовать read в while цикле, как это напечатать первый столбец, т.е. имя пользователя:

last | sed '$ d' | while read -r u _; do 
    echo "$u" 
done 
Смежные вопросы