bash меняет свое поведение в зависимости от значения переменной «IFS»

18

Когда я устанавливаю IFSпеременную в пробел, bashобрабатывает несколько пробелов как один пробел ( myprogramэто программа, которая печатает полученные аргументы командной строки):

IFS=" "
x="hello   hi   world"
./myprogram $x
argv[1] = hello
argv[2] = hi
argv[3] = world

Но когда я устанавливаю IFSпеременную на запятую, bashнесколько запятых не обрабатываются как одна запятая:

IFS=","
x="hello,,,hi,,,world"
./myprogram $x
argv[1] = hello
argv[2] = 
argv[3] = 
argv[4] = hi
argv[5] = 
argv[6] = 
argv[7] = world

Это почему?

user267935
источник
Просто для справки, «IFS» означает внутренний разделитель полей .
pr1268

Ответы:

21

Это задокументировано в man bash. Единственное вхождение любого символа в IFS, который не является пробелом, ограничивает поле.

От man bash:

Оболочка обрабатывает каждый символ IFS как разделитель и разбивает результаты других расширений на слова, используя эти символы в качестве разделителей полей. Если IFS не установлен, или его значение в точности <space><tab><newline>, по умолчанию, то последовательность <space>, <tab>и <newline>в начале и в конце результатов предыдущих расширений, игнорируется, а любая последовательность символов МФСА не в начале или в конце служит для определения границ слова. Если IFS имеет значение, отличное от значения по умолчанию, то последовательности пробельных символов пробела, табуляции и новой строки игнорируются в начале и конце слова, если символ пробела находится в значении IFS (символ пробела IFS ). Любой символ в IFS, который не является пробелом IFS, вместе с любыми соседними символами пробела IFS разделяет поле. Последовательность пробельных символов IFS также рассматривается как разделитель. Если значение IFS равно нулю, разделение слов не происходит. [Акцент добавлен.]

Примеры: разделение полей

Если IFS не имеет пробельных символов, тогда пробел включается в поля:

$ ( IFS=',' x='one , two,three'; printf "<%s>\n" $x )
<one >
< two>
<three>

Если IFS имеет и пробелы, и запятую, то последовательности пробелов, за которыми следует запятая, а затем последовательности пробелов, рассматриваются как один разделитель:

$ ( IFS=' ,' x='one , two,three'; printf "<%s>\n" $x )
<one>
<two>
<three>

Последовательности запятых интерпретируются как последовательности пустых полей:

$ ( IFS=' ,' x='one,,,two,three'; printf "<%s>\n" $x )
<one>
<>
<>
<two>
<three>

Примеры: пробелы в начале и в конце

Если IFS не содержит пробелов, тогда любые пробелы в начале и конце сохраняются в полях:

$ ( IFS=',' x='  one , two,three  ,'; printf "<%s>\n" $x )
<  one >
< two>
<three  >

Если IFS содержит пробелы, то удаляются все начальные или конечные последовательности пробелов:

$ ( IFS=' ,' x='  one , two,three  ,'; printf "<%s>\n" $x )
<one>
<two>
<three>
John1024
источник
возможно, стоит также подчеркнуть, что «тогда последовательности пробельных символов пробела, табуляции и новой строки игнорируются в начале и конце слова, если символ пробела находится в значении IFS»
Джефф Шаллер
@JeffSchaller Отличная идея: я только что добавил раздел об этом.
John1024
Что делать, если у вас есть файл с разделителями табуляции с некоторыми пропущенными значениями? т.е. вы не хотите, чтобы последовательности вкладок рассматривались как одна вкладка. Кроме того, поля содержат запятые, поэтому их нельзя использовать в качестве разделителя. Является ли единственным решением использовать какой-либо другой разделитель (не табуляцию)?
Давос
@Davos Для данных с каждым полем, разделенным одной вкладкой, может быть более естественным использовать другие инструменты, которые легко справляются с этим, например, awkс помощью -F'\t'опции или cut. С другой стороны , если у вас есть последняя версия bash, вы можете быть в состоянии разобрать поля , используя readarrayс -d$'\t'опцией.
John1024