- Bash split string into array using 4 simple methods
- Sample script with variable containing strings
- Method 1: Bash split string into array using parenthesis
- Method 2: Bash split string into array using read
- Method 3: Bash split string into array using delimiter
- Method 4: Bash split string into array using tr
- Some more examples to convert variable into array in bash
- Related Posts
- 4 thoughts on “Bash split string into array using 4 simple methods”
- Bash Split String
- Bash Split String
- Split String with single character delimiter(s) in Bash using IFS
- Example 1: Bash Split String by Space
- Example 2: Bash Split String by Symbol
- Split String with multiple character delimiter
- Example 3: Split String with another string as delimiter idiomatic expressions
- Example 4: Bash Split a String with multiple character delimiter
- Conclusion
- How to Split String in Bash Script
- Method 1: Split string using read command in Bash
- Method 2: Split string using tr command in Bash
- Разбить строку в массив в bash
- 15 ответов
Bash split string into array using 4 simple methods
Table of Contents
How to create array from string with spaces? Or In bash split string into array? We can have a variable with strings separated by some delimiter, so how to split string into array by delimiter?
The main reason we split string into array is to iterate through the elements present in the array which is not possible in a variable.
Sample script with variable containing strings
For example in this script I have a variable myvar with some strings as element
Here if I want to iterate over individual element of the myvar variable, it is not possible because this will considered as a variable and not an array.
This we can verify by counting the number of elements in the myvar variable
When we execute the script, we see that number of elements in myvar is 1 even when we have three elements
To overcome this we convert and split string into array. Now your variable can have strings or integers or some special characters, so depending upon your requirement you can choose different methods to convert string into an array. I will cover some of them with examples:
Method 1: Bash split string into array using parenthesis
Normally to define an array we use parenthesis () , so in bash to split string into array we will re-define our variable using open and closed parenthesis
Snippet from my terminal
Method 1
Next execute the shell script. We see know we have 3 elements in the array
Method 2: Bash split string into array using read
We can use read -a where each input string is an indexed as an array variable.
the default delimiter is considered as white space so we don’t need any extra argument in this example:
Snippet from my terminal
Method 2
Execute the script. Now the myarray contains 3 elements so bash split string into array was successful
Method 3: Bash split string into array using delimiter
We can combine read with IFS (Internal Field Separator) to define a delimiter.
Assuming your variable contains strings separated by comma character instead of white space as we used in above examples
We can provide the delimiter value using IFS and create array from string with spaces
Snippet from my terminal
Method 3
Execute the shell script, and the variable is successfully converted into array and the strings can be iterated separately
Method 4: Bash split string into array using tr
tr is a multi purpose tool. We will use this tool to convert comma character into white space and further using it under parenthesis from Method 1 to create array from string with spaces
So you can use this with any other delimiter, although it may not work under all use cases so you should verify this based on your requirement
Snippet from my terminal
Method 4
Execute the shell script
Some more examples to convert variable into array in bash
Based on your requirement you can choose the preferred method.
For example here I have a variable with newline as delimiter. So here I can use the first method. This will create array from string with spaces
Execute the script. You can verify using the number of elements in the array
We can now iterate through the elements which are part of the array in a loop
Execute the script to verify
Lastly I hope the steps from the article for bash split string into array on Linux was helpful. So, let me know your suggestions and feedback using the comment section.
Related Posts
Didn’t find what you were looking for? Perform a quick search across GoLinuxCloud
If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.
For any other feedbacks or questions you can either use the comments section or contact me form.
Thank You for your support!!
4 thoughts on “Bash split string into array using 4 simple methods”
Tks. Great. Can we use the array element “$
Can you please give an example so I can help you
I found Method 3 did not work for me… Here is the slightly modifed script:
(Modified to show the bash version in use and the contents of the first array entry…)
Here is what I get:
It is possibly because my bash version is prehistoric, but I remain surprised that I get no error messages. Interesting the commas have gone when it lists My array[0] despite IFS initialisation…
Источник
Bash Split String
Bash Split String – Often when working with string literals or message streams, we come across a necessity to split a string into tokens using a delimiter. The delimiter could be a single character or a string with multiple characters. In this tutorial, we shall learn how to split a string in bash shell scripting with a delimiter of single and multiple character lengths.
Bash Split String
Split String with single character delimiter(s) in Bash using IFS
To split a string in bash using IFS, follow the below steps:
Step 1: Set IFS to the delimiter you would want. IFS=’ ‘ IFS is an internal variable that determines how Bash recognizes word boundaries. The default value of IFS is white space. If you set it to some other value, reset it to default whitespace.
Step 2: Read your string to a variable with options -ra. read -ra ARR
Option | Description |
---|---|
-r | Backslash does not act as an escape character. |
-a ARR | The words(separated by IFS) are assigned to the sequential index of array ARR beginning at zero. |
Now you have your string split by the delimiter (set in IFS) stored in array ARR. ARR is just array name. Any string literal that is valid could be used as an array name.
Step 3: You may now access the tokens split into an array using a bash for loop.
Its time for some examples. In the following two examples, we will go through example bash scripts where we use IFS to split a string.
Example 1: Bash Split String by Space
In this example, we split a string using space as a character delimiter.
Bash Script File
Run the above bash shell script in a terminal.
Output
Example 2: Bash Split String by Symbol
Sometimes we may need to split a string by a delimiter other than space. To split a string in bash shell by a symbol or any other character, set the symbol or specific character to IFS and read the string to a variable with the options -ra mentioned in the below example.
Bash Script File
Run the above bash shell script in terminal.
Output
The default value of IFS is single space ‘ ‘ . We changed the value of IFS to split a string. It should not affect any further script that is dependent on IFS. So, assign the default value back to IFS.
Split String with multiple character delimiter
To split a string with a multiple character delimiter (or simply said another string), following are two of the many possible ways, one with idiomatic and the other with just basic bash if and bash while loop.
Example 3: Split String with another string as delimiter idiomatic expressions
In this example, we will use idiomatic expressions where parameter expansion is done, and thus a very compact script.
Bash Script File
When you run the above script in a Bash Shell, you will see an output similar to the following
Output
Following Parameter-Expansions are used (Reference: Bash Man Page[https://linux.die.net/man/1/bash])
Expansion | Description |
$ | Remove the longest matching suffix pattern. |
$ | Remove shortest matching prefix pattern. |
Example 4: Bash Split a String with multiple character delimiter
If you are new to bash shell scripting and are not familiar with idiomatic expressions, following is an easily understandable shell script with basic bash if, bash while and bash substring methods. Comments are provided at each step to make the script file more readable.
Bash Script File
Output
The split strings are stored in the array and could be accessed using an index.
Conclusion
In this Bash Tutorial, we have learned how to split a string using bash script with different scenarios based on delimiter: like single character delimiter and multiple character delimiter.
Источник
How to Split String in Bash Script
Let’s say you have a long string with several words separated by a comma or underscore. You want to split this string and extract the individual words.
You can split strings in bash using the Internal Field Separator (IFS) and read command or you can use the tr command. Let me show you how to do that with examples.
Method 1: Split string using read command in Bash
Here’s my sample script for splitting the string using read command:
The part that split the string is here:
Let me explain it to you. IFS determines the delimiter on which you want to split the string. In my case, it’s a semi colon. It could be anything you want like space, tab, comma or even a letter.
The IFS in the read command splits the input at the delimiter. The read command reads the raw input (option -r) thus interprets the backslashes literally instead of treating them as escape character. The option -a with read command stores the word read into an array in bash.
In simpler words, the long string is split into several words separated by the delimiter and these words are stored in an array.
Now you can access the array to get any word you desire or use the for loop in bash to print all the words one by one as I have done in the above script.
Here’s the output of the above script:
Method 2: Split string using tr command in Bash
This is the bash split string example using tr (translate) command:
This example is pretty much the same as the previous one. Instead of the read command, the tr command is used to split the string on the delimiter.
The problem with this approach is that the array element are divided on ‘space delimiter’. Because of that, elements like ‘Linux Mint’ will be treated as two words.
Here’s the output of the above script:
That’s the reason why I prefer the first method to split string in bash.
I hope this quick bash tutorial helped you in splitting the string. In a related post, you may also want to read about string comparison in bash.
And if you are absolutely new to Bash, read our Bash beginner tutorial series.
Источник
Разбить строку в массив в bash
в скрипте Bash я хотел бы разделить строку на части и сохранить их в массиве.
Я хотел бы иметь их в массив, как это:
Я хотел бы использовать простой код, скорость команды не имеет значения. Как я могу это сделать?
15 ответов
обратите внимание, что символы $IFS рассматриваются индивидуально как разделители, так что в этом случае поля могут быть разделены на или запятая или пробел, а не последовательность двух символов. Интересно, что пустые поля не создаются при появлении запятых во входных данных, потому что пространство обрабатывается специально.
для доступа к отдельному элементу:
для перебора элементы:
чтобы получить индекс и значение:
последний пример полезен, потому что массивы Bash разрежены. Другими словами, вы можете удалить элемент или добавить элемент, а затем индексы не будут смежными.
получить количество элементов в массиве:
как упоминалось выше, массивы могут быть разреженными, поэтому вы не должны использовать длину для получения последнего элемента. Вот как вы можете в Bash 4.2 и позже:
в любой версии Bash (откуда-то после 2.05 b):
большие отрицательные смещения выберите дальше от конца массива. Обратите внимание на пробел перед знаком минус в старой форме. Это необходимо.
вот способ без установки IFS:
идея заключается в использовании замены строки:
чтобы заменить все совпадения $substring пробелом, а затем использовать замещенную строку для инициализации массива:
Примечание: этот ответ использует split+glob оператор. Таким образом, чтобы предотвратить расширение некоторых символов (например, * ) это хорошая идея, чтобы приостановить подстановка для этого сценария.
все ответы на этот вопрос являются неправильными в той или иной степени.
1: это злоупотребление $IFS . Значение $IFS переменная не взят одной переменной длиной разделитель строк, скорее он берется как set of один символов строковые сепараторы, где каждое поле что read разделение от входной линии может быть прекращено любой символ в наборе (запятая или space, в этом примере).
на самом деле, для настоящих приверженцев там, полное значение $IFS немного сложнее. От руководство bash:
оболочка обрабатывает каждый символ ИФС в качестве разделителя, и разбивает результаты из других расширений в слова, используя эти символы как терминаторы полей. Если ИФС не установлено, или его значение точно , по умолчанию, затем последовательности , и в начале и конце результатов предыдущих расширений игнорируются, и любая последовательность ИФС символы в начале или конце служит для разделения слов. Если ИФС имеет значение, отличное от значения по умолчанию, а затем последовательности пробелов , и игнорируются в начале и конце слова, пока символ пробела находится в значении ИФС (an ИФС символ пробела). Любой символ в ИФС это не ИФС пробел вместе с рядом ИФС пробелы, разделяющие поле. Последовательность ИФС пробелы также рассматриваются как разделитель. Если значение ИФС равно null, разделение слов не происходит.
в основном, для нестандартных ненулевых значений $IFS , поля могут быть разделены либо (1) последовательностью одного или нескольких символов, которые все из набора «IFS пробелов» (то есть, какой из , и («строки», Что означает строки (LF)) присутствуют в любом месте $IFS ), или (2) Любой не-«символ пробела IFS», который присутствует в $IFS вместе с любыми «символами пробелов IFS» окружают его во входной строке.
для OP возможно, что второй режим разделения, который я описал в предыдущем абзаце, — это именно то, что он хочет для своей входной строки, но мы можем быть уверены, что первый режим разделения, который я описал, совсем не правильный. Например, что, если его входная строка была ‘Los Angeles, United States, North America’ ?
2: даже если вы должны были использовать это решение с односимвольный разделитель (например, запятую сама по себе, то есть без космических или другого багажа), если значение $string переменная содержит любые LFs, затем read остановит обработку, как только он встретит первый LF. Этот read builtin обрабатывает только одну строку на вызов. Это верно, даже если вы трубопровод или перенаправление ввода только до read заявление, как мы делаем в этом примере с строка механизм, и таким образом гарантирован, что будет потерян необработанный входной сигнал. Код, который питает read builtin не имеет знаний о потоке данных в своей командной структуре.
вы можете возразить, что это вряд ли вызовет проблема, но все же, это тонкая опасность, которую следует избегать, если это возможно. Это вызвано тем, что read builtin фактически выполняет два уровня разделения ввода: сначала на строки, затем на поля. Поскольку OP хочет только один уровень разделения, это использование read builtin не подходит, и мы должны избегать этого.
3: неочевидная потенциальная проблема с этим решением заключается в том, что read всегда удаляет поле трейлинга, если оно пустое, хотя в противном случае он сохраняет пустые поля. Вот демо:
возможно, ОП не будет заботиться об этом, но это все еще ограничение, о котором стоит знать. Это снижает надежность и универсальность решения.
эта проблема может быть решена путем добавления фиктивного конечного разделителя к входной строке непосредственно перед подачей его в read , как я покажу позже.
(Примечание: я добавил отсутствующие скобки вокруг подстановки команды, которую ответчик, похоже, опустил.)
эти решения используют разделение слов в назначении массива для разделения строки на поля. Как ни странно, прямо как read , общее разделение слов также использует $IFS специальная переменная, хотя в этом случае подразумевается, что она имеет значение по умолчанию , и поэтому любая последовательность одного или нескольких символов IFS (которые теперь являются символами пробелов) считается разделителем полей.
это решает проблему двух уровней разделения, совершенного read , так как расщепление слова само по себе составляет только один уровень расщепления. Но, как и раньше, проблема в том, что отдельные поля во входной строке уже могут содержать $IFS символы, и, таким образом, они будут неправильно разделены во время операции разделения слов. Это не относится ни к одной из выборочных входных строк, предоставляемых этими ответчиками (насколько это удобно. ), но, конечно, это не меняет того факта, что любая кодовая база, которая использовала эту идиому, рисковала бы взорваться, если бы это предположение когда-либо было нарушено в какой-то момент вниз по линии. Еще раз рассмотреть мое контрпример ‘Los Angeles, United States, North America’ (или ‘Los Angeles:United States:North America’ ).
кроме того, разделение слов обычно сопровождается расширение имени файла (ака расширения путем ака globbing), который, если это будет сделано, потенциально испортит слова, содержащие символы * , ? или [ следовал по ] (и, если extglob установлен, в скобках фрагменты предшествуют ? , * , + , @ или ! ), сопоставляя их против объектов файловой системы и расширения слов («globs») соответственно. Первый из этих трех ответчиков умело подрезал эту проблему, запустив set -f заранее, чтобы отключить globbing. Технически это работает (хотя вы, вероятно, должны добавить set +f потом включить подстановки для последующего кода, который может зависеть от этого), но это нежелательно связываться с глобальными настройками оболочки для того, чтобы взломать основной строку в массив при разборе операции в местной код.
еще одна проблема с этот ответ заключается в том, что все пустые поля будут потеряны. Это может быть или не быть проблемой, в зависимости от приложения.
Примечание: Если вы собираетесь использовать это решение, то лучше использовать $
этот ответ почти такой же, как #2. Разница в том, что ответчик сделал предположение, что поля разделены двумя символами, один из которых представлен в default $IFS , а другой нет. Он решил этот довольно конкретный случай, удалив символ, не представленный IFS, используя расширение подстановки шаблона, а затем использование разбиения слов для разбиения полей на уцелевший символ разделителя, представленный IFS.
это не очень универсальное решение. Кроме того, можно утверждать, что запятая действительно является «первичным» символом разделителя здесь, и что ее удаление, а затем в зависимости от символа пространства для расщепления поля просто неправильно. Еще раз рассмотрим мой контрпример:—34—>.
кроме того, опять же, расширение имени файла может поврежден расширенной слова, но это может быть предотвращено путем временного отключения подстановка на назначение с set -f а то set +f .
кроме того, опять же, все пустые поля будут потеряны, что может быть или не быть проблемой в зависимости от приложения.
это похоже на #2 и #3 в том, что он использует разделение слов, чтобы выполнить работу, только теперь код явно устанавливает $IFS содержать только односимвольный разделитель полей, присутствующий во входной строке. Следует еще раз отметить, что это не может работать для многосимвольная поле разделители, такие как операция запятая-разделитель пространства. Но для односимвольного разделителя, такого как LF, используемый в этом примере, он фактически приближается к совершенству. Поля не могут быть непреднамеренно разделены посередине, как мы видели с предыдущими неправильными ответами, и существует только один уровень расщепления, как требуется.
одна из проблем заключается в том, что расширение имени файла повредит затронутые слова, как описано ранее, хотя еще раз это можно решить, обернув критическое утверждение в set -f и set +f .
еще одна потенциальная проблема заключается в том, что, поскольку LF квалифицируется как «символ пробела IFS», как определено ранее, все пустые поля будут потеряны, как и в #2 и #3. Это было бы конечно, это не проблема, если разделитель не является «символом пробела IFS», и в зависимости от приложения это может не иметь значения, но это искажает общность решения.
Итак, подводя итог, предполагая, что у вас есть односимвольный разделитель, и это либо не «символ пробела IFS», либо вам не нужны пустые поля, и вы обертываете критическое утверждение в set -f и set +f , то это решение работает, но в противном случае не.
(кроме того, для информации, назначение LF переменной в bash может быть сделано более легко с помощью $’. ‘ синтаксис, например, IFS=$’\n’; .)
такое решение эффективно помесь #1 (в том, что он устанавливает $IFS в запятую) и #2-4 (в том, что он использует разделение слов для разделения строки на поля). Из-за этого он страдает от большинства проблем, которые затрагивают все вышеперечисленные неправильные ответы, как худший из всех миров.
кроме того, что касается второго варианта, может показаться, что eval вызов совершенно не нужен, так как его аргумент является строковым литералом с одной кавычкой и поэтому статически известен. Но на самом деле есть очень неочевидная польза от использования eval таким образом. Обычно при выполнении простой команды, состоящей из переменной assignment только, что означает, что без фактического командного слова после него, назначение вступает в силу в среде оболочки:
это верно, даже если простая команда включает несколько назначения переменных; опять же, пока нет командного слова, все назначения переменных влияют на оболочку среды:
но, если присвоение переменной прикреплено к имени команды (мне нравится называть это «присвоением префикса»), то это делает не влияет на среду оболочки и вместо этого влияет только на среду выполняемой команды, независимо от того, является ли она встроенной или внешней:
соответствующая цитата из руководство bash:
если нет результатов имя команды, переменная назначения влияют на текущую среду оболочки. В противном случае переменные добавляются в среду выполняемой команды и не влияют на текущую среду оболочки.
можно использовать эту функцию присвоения переменных для изменения $IFS только временно, что позволяет нам избежать всего Гамбита сохранения и восстановления, как это делается с $OIFS переменная в первом варианте. Но трудность здесь заключается в том, что команда нам нужно запустить само по себе простое назначение переменной, и, следовательно, оно не будет включать командное слово, чтобы сделать $IFS назначение временное. Вы можете подумать про себя, почему бы просто не добавить командное слово no-op к оператору, такому как : builtin сделать $IFS назначение временно? Это не работает, потому что тогда он сделает $array назначение временное, а также:
Итак, мы фактически в тупике, немного Уловка-22. Но когда . —82—> запускает свой код, он запускает его в среде оболочки, как если бы это был обычный статический исходный код, и поэтому мы можем запустить $array назначение внутри eval аргумент, чтобы он вступил в силу в среде оболочки, в то время как $IFS назначение префикса, который начинается с eval команда не переживет . Это именно тот трюк, который используется во втором варианте этого решения:
Итак, как вы видите, на самом деле это довольно умный трюк и выполняет именно то, что требуется (по крайней мере, в отношении выполнения задания) довольно неочевидным способом. Я на самом деле не против этого трюка в целом, несмотря на участие eval ; просто будьте осторожны, чтобы цитировать строку аргумента для защиты от угроз безопасности.
но опять же, из-за» худшего из всех миров » агломерации проблем, это все еще неправильный ответ на ОП требование.
Хм. что? ФП имеет строковую переменную, которая должна быть проанализирована в массив. Этот «ответ» начинается с дословного содержимого входной строки, вставленной в литерал массива. Думаю, это один из способов.
похоже, ответчик мог предположить, что $IFS переменная влияет на весь анализ bash во всех контекстах, но это неправда. Из руководства bash:
ИФС разделитель внутреннего поля, который используется для разделения слов после расширения и разделения строк на слова с помощью читать встроенная команда. Значение по умолчанию: .
так $IFS специальная переменная фактически используется только в двух контекстах: (1) разделение слов, которое выполняется после расширение (что означает не при разборе исходного кода bash) и (2) для разделения входных строк на слова по read встроенные.
позвольте мне попытаться сделать это яснее. Я думаю, было бы неплохо провести различие между извлечение и исполнение. Баш должен сначала парсить исходный код, который, очевидно, является извлечение событие, а потом выполняет код, именно тогда на сцену выходит экспансия. Расширение действительно исполнение событие. Кроме того, я не согласен с описанием $IFS переменная, которую я только что процитировал выше; вместо того, чтобы говорить, что разделение слов выполняется после расширения, я бы сказал, что разбиение производится во время расширение, или, возможно, даже более точно, разделение слов часть процесс расширения. Фраза » слово разделение » относится только к этому шагу расширения; он никогда не должен использоваться для ссылки на синтаксический анализ исходного кода bash, хотя, к сожалению, документы, похоже, бросают вокруг слов «split» и «words» много. Вот соответствующий отрывок из linux.die.net версия руководства bash:
расширение выполняется в командной строке после ее разбиения на слова. Существует семь видов расширения:бандаж расширение, расширения , расширение параметров и переменных, команду, арифметические расширения, разбиение и расширения путем.
порядок расширений: расширение скобки; расширение Тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разделение слов; и расширение пути.
вы могли бы поспорить версия GNU руководства делает немного лучше, так как он выбирает слово «токены» вместо «слова» в первом предложении раздела расширения:
расширение выполняется в командной строке после ее разделения на токены.
важный момент, $IFS не изменяет способ анализа исходного кода bash. Разбор источника bash код на самом деле очень сложный процесс, который включает в себя распознавание различных элементов грамматики оболочки, таких как последовательности команд, списки команд, конвейеры, расширения параметров, арифметические замены и замены команд. По большей части процесс синтаксического анализа bash не может быть изменен действиями на уровне пользователя, такими как назначения переменных (на самом деле, есть некоторые незначительные исключения из этого правила; например, см. various compatxx настройки оболочки, который может изменить некоторые аспекты разбора поведения на лету). Восходящие «слова» / «токены», которые являются результатом этого сложного процесса синтаксического анализа, затем расширяются в соответствии с общим процессом» расширения», как разбито в приведенных выше выдержках документации, где разбиение расширенного (расширение?) текст в нижестоящие слова-это просто один шаг этого процесса. Разбиение слов касается только текста, который был выплюнут из предыдущего шага расширения; это не влияет на буквальный текст, который был проанализирован правильно отключен от источника bytestream.
это одно из лучших решений. Обратите внимание, что мы вернулись к использованию read . Разве я не говорил раньше, что read неуместно, потому что он выполняет два уровня расщепления, когда нам нужен только один? Фокус здесь в том, что вы можете позвонить read таким образом, что он эффективно выполняет только один уровень расщепления, в частности разделение только одного поля на вызов, что требует затрат на повторный вызов в цикле. Это немного ловко, но работает.
но есть проблемы. Во-первых: когда вы предоставляете хотя бы один имя до read , он автоматически игнорирует ведущие и конечные пробелы в каждом поле, которое отделено от входной строки. Это происходит ли $IFS имеет значение по умолчанию или нет, как описано ранее в эта должность. Теперь OP может не заботиться об этом для своего конкретного случая использования, и на самом деле это может быть желательной особенностью поведения синтаксического анализа. Но не каждый, кто хочет разобрать строку на поля, захочет этого. Однако есть решение: несколько неочевидное использование read должен пройти ноль имя аргументов. В этом случае read будет хранить всю входную строку, которую она получает из входного потока в переменной с именем $REPLY , и, как бонус, это делает не полоса ведущих и конечных пробелов от значения. Это очень надежное использование read который я часто использовал в своей карьере программирования оболочки. Вот демонстрация различия в поведении:
вторая проблема с этим решением заключается в том, что оно фактически не затрагивает случай пользовательского разделителя полей, такого как запятая OP. Как и раньше, разделители multicharacter не поддерживаются, что является неудачным ограничение этого решения. Мы могли бы попытаться хотя бы разделить запятую, указав разделитель на -d вариант, но посмотрите, что происходит:
предсказуемо, неучтенные окружающие пробелы были втянуты в значения поля, и, следовательно, это должно было быть исправлено впоследствии посредством операций обрезки (это также можно было сделать непосредственно в цикле while). Но есть еще одна очевидная ошибка: Европа отсутствует! Что с ним случилось? Ответ таков: read возвращает неудачный код возврата, если он попадает в конец файла (в этом случае мы можем назвать его концом строки), не сталкиваясь с окончательным Терминатором поля в последнем поле. Это приводит к преждевременному разрыву цикла while, и мы теряем последнее поле.
технически эта же ошибка затронула и предыдущие примеры; разница в том, что разделитель полей был принят за LF, что является значением по умолчанию, когда вы не указываете и («here-string») механизм автоматически добавляет LF к строке непосредственно перед тем, как он подает ее в качестве ввода в команду. Следовательно, в этих случаях, мы вроде случайно решил проблему отброшенного конечного поля, невольно добавив дополнительный фиктивный Терминатор к входу. Назовем это решение решением «манекен-Терминатор». Мы можем применить решение dummy-terminator вручную для любого пользовательского разделителя, объединив его с входной строкой самостоятельно, когда создание экземпляра в here-string:
нет, проблема решена. Другим решением является только разрыв цикла while, если оба (1) read возвращенный отказ и (2) $REPLY пусто, смысла read не удалось прочитать какие-либо символы до нажатия конца файла. Demo:
этот подход также показывает секретный LF, который автоматически добавляется к строке here оператор перенаправления. Конечно, его можно было бы снять. отдельно через явную операцию обрезки, как описано минуту назад, но, очевидно, ручной подход манекена-Терминатора решает его напрямую, поэтому мы могли бы просто пойти с этим. Ручное решение манекена-Терминатора на самом деле довольно удобно в том, что оно решает обе эти две проблемы (проблему отброшенного конечного поля и добавленную проблему LF) за один раз.
Итак, в целом, это довольно мощное решение. Это единственная оставшаяся слабость-отсутствие поддержки многосимвольная разделители, которые я затрону позднее.
(это на самом деле с того же поста, что и #7; ответчик предоставил два решения в одном и том же сообщении.)
на readarray builtin, что является синонимом mapfile , это идеальный вариант. Это встроенная команда, которая анализирует bytestream в переменную массива за один снимок; нет возиться с петлями, условностями, заменами или чем-то еще. И он не тайно удаляет пробелы из входной строки. И (если -O не дается) он удобно очищает целевой массив перед назначением ему. Но это все еще не идеально, поэтому моя критика его как «неправильного ответа».
во-первых, просто чтобы убрать это с пути, обратите внимание, что, как и поведение read при выполнении анализа полей, readarray удаляет поле трейлинга, если оно пустое. Опять же, это, вероятно, не касается OP, но это может быть для некоторых случаев использования. Я вернусь к этому вопросу.
во-вторых, как и раньше, он не поддерживает разделители multicharacter. Я тоже сейчас все исправлю.
в-третьих, решение, как написано, не анализирует входную строку OP, и на самом деле его нельзя использовать как-есть для ее анализа. Я и об этом сейчас расскажу.
по вышеуказанным причинам я все еще считаю это будет «неправильный ответ» на вопрос ОП. Ниже я дам то, что считаю правильным ответом.
правильный ответ
вот наивная попытка сделать #8 работа, просто указав :
мы видим, что результат идентичен результату, который мы получили от двойного условного подхода цикла read решение обсуждалось в #7. Мы можем почти решите это с помощью ручного трюка манекена-Терминатора:
проблема здесь в том, что readarray сохранено поле трейлинга, так как оператор перенаправления добавил LF к входной строке, и поэтому конечное поле было не пустой (в противном случае он был бы сброшен). Мы можем позаботиться об этом явно сбросили последний элемент массива после:
только две проблемы, которые остаются, которые на самом деле связаны, являются (1) лишние пробелы, требующие проверки, и (2) Отсутствие поддержки многосимвольная разделители.
пробелы, конечно, могут быть обрезаны позже (например, см. Как обрезать пробелы из переменной Bash?). Но если мы сможем взломать многохарактерный разделитель, то это решит обе проблемы за один выстрел.
к сожалению, нет прямые способ получить разделитель multicharacter для работы. Лучшее решение я придумал это для предварительной обработки входной строки, чтобы заменить многосимвольная разделитель один разделитель, который будет гарантировано не столкнуться с содержимым строке ввода. Единственным символом, который имеет эту гарантию, является нул байт. Это связано с тем, что в bash (хотя и не в zsh, кстати) переменные не могут содержать байт NUL. Этот шаг предварительной обработки можно выполнить в процесс замещения. Вот как это сделать с помощью на awk:
вот, наконец-то! Это решение не будет ошибочно разделять поля посередине, не будет вырезаться преждевременно, не будет отбрасывать пустые поля, не будет коррумпировать себя на расширениях имени файла, не будет автоматически удалять ведущие и конечные пробелы, не оставит безбилетника LF на конце, не требует циклов и не удовлетворяется одним символом ограничитель данных.
решение для обрезки
наконец, я хотел продемонстрировать свое собственное довольно сложное решение для обрезки, используя неясное на readarray . К сожалению, у меня закончилось место против драконовского 30,000-символьного ограничения переполнения стека, поэтому я не смогу это объяснить. Я оставлю это упражнение для читателя.
Источник