Как заставить ksh читать пустые поля

У меня есть файл с разделителями табуляции, в котором некоторые поля потенциально не содержат данных. В ksh «чтение» рассматривает несколько вкладок как один разделитель. Есть ли способ изменить это поведение, чтобы у меня тоже были пустые данные? т.е. При встрече с двумя вкладками это будет восприниматься как нулевое поле? Или я должен использовать awk?

# where <TAB> would be a real tab:
while IFS="<TAB>" read a b c d; do echo $c; done < file.txt

cf.

awk -F"\t" '{print $3}' file.txt

Версия оболочки выведет неправильное поле, если 1-я или 2-я запись пуста.


person Ban Atman    schedule 26.02.2015    source источник


Ответы (2)


Действительно, можно использовать современную оболочку Korn изначально для обработки каждого символа табуляции как разделителя столбцов, так что несколько последовательных табуляции будут разделять пустые поля без sed, awk или perl. Хитрость заключается в том, чтобы установить переменную IFS на 2 последовательных символа табуляции, например:

IFS=$'\t\t'

Цикл while в следующем коде считывает файл значений, разделенных табуляцией, помещая поля каждой строки в простой индексированный массив. Внутренний цикл for просто выводит прочитанное, по одному полю на строку вывода:

typeset -a Cols

while IFS=$'\t\t' read -A Cols
do
    for (( i=0 ; i < ${#Cols[@]} ; i++ ))
    do
        print "Cols[$i] '${Cols[$i]}' "
    done
done

И да, это также будет корректно рассматривать строку, начинающуюся с символа табуляции, как имеющую нулевое значение для столбца 1, т. е. в приведенном выше Cols[0] будет установлено значение нулевой.

Я протестировал это на /bin/ksh 'AJM93u+ 2012-08-01' на macOS High Sierra, но оно должно работать с версиями ksh с открытым исходным кодом AT&T AST, выпущенными 10 или более лет назад. См. также https://github.com/att/ast.

person Mario D    schedule 20.06.2018

read будет искать первое поле, игнорируя IFS. Другой демонстрацией этой проблемы является

echo " b c d e"  | while read a b c d e; do echo c=$c; done

Я продолжу использовать пробел в качестве IFS, просто его немного легче тестировать.
Можно избежать awk с помощью cut:

echo c=$(echo " b c d e"  | cut -d" " -f3)

Если вы хотите назначить все поля за один прогон, вы застрянете с cut.
Sed принимает различные опции -e и работает с ними в указанном порядке. Вы можете получить поля по

eval $(echo " b c d e"  | 
   sed -e 's/^/a=/' -e 's/ /;b=/' -e 's/ /;c=/' -e 's/ /;d=/' -e 's/ /;e=/')
echo check:
set | grep "^[a-e]="

Вы доверяете своему вводу или предпочитаете awk sed?

person Walter A    schedule 26.02.2015