Skip to content

Instantly share code, notes, and snippets.

@Jeff-Russ
Last active May 26, 2016 19:43
Show Gist options
  • Save Jeff-Russ/3f96a0ecf0fae4a5690e368284b4ec08 to your computer and use it in GitHub Desktop.
Save Jeff-Russ/3f96a0ecf0fae4a5690e368284b4ec08 to your computer and use it in GitHub Desktop.

Study: Bash fails: splitting strings

#!/bin/sh
# This is supposed to iterate each line, printing the next
# line with each iteration along with it's length:
split_into_lines () {
local i=0
while read -r line; do
printf "Line $1 is: '$line' "
local len=${#line}
printf " It's $len characters long\n"
let "i = i + 1"
done <<< "$string"
}
echo
## TEST 1 ----------------------------------------------------------------
# This actually works if you create "newlines" in a sort of heredoc style:
string="123 567
123 56
12 45"
split_into_lines $string # outputs:
# Line 123 is: '123 567' It's 7 characters long
# Line 123 is: '123 56' It's 6 characters long
# Line 123 is: '12 45' It's 5 characters long
echo
## TEST 2 ----------------------------------------------------------------
# But apparently that's not the same as actually having \n in the string.
# Here is the same string but with \n instead of actual new lines:
string="123 567\n123 56\n12 45"
split_into_lines $string # outputs:
: "
Line 123 is: '123 567
123 56
12 45' It's 22 characters long
It prints with newlines (\n's are honored)
but the only iterates once (\n's are not honored)
-----------------------------------------------------------------------
When you might normally prefer to use \n over actual new lines just so
you can compact or just honor normal indenting in your code. But this:
"
some_func () {
string="123 567
123 56
12 45"
# blah blah blah
}
# and even this:
string="123 567
123 56
12 45"
: "
will have the exact same result since bash will crap
all over your whitespace and totally ignore it anyway.
It will ignore if you type \n but not ignore when you actually
hit return.. and then go back to ingoring you if you hit
tab or even the space bar.
Great.
"
echo
## Plan B ----------------------------------------------------------------
# This actually works in splitting up our array, now defined like this:
string="123 567\n123 56\n12 45"
# http://stackoverflow.com/questions/19190967/shell-temp-ifs-as-newline-only-why-doesnt-this-work-ifs-echo-e-n
split_at_esc_n () {
(
IFS=$'\n'
echo -n "$IFS" | od -t x1 &> /dev/null
local i=0
for line in `printf $string`; do
printf "Line $i is '$line' "
local len=${#line}
printf " It's $len characters long\n"
let "i = i + 1"
done
)
}
split_at_esc_n $string # outputs:
: "
Line 0 is: 123 567 It's 7 characters long
Line 1 is: 123 56 It's 6 characters long
Line 2 is: 12 45 It's 5 characters long
CONCLUSION ----------------------------------------------------------
We stil don't have any way of actually saving the output to an array in
either case. If you try you'll get a buchered up array with new indexes
at any whitespace (each word is an element). This just means we can't make
a function out of it so we're out of luck in the area of code reuse.
But if all you need the array for is manipulation of each line separately
wecan, however, take either of these approaches and just put whatever it
is you want to do in the loop. That's probably the best approach.
Or just use something like Ruby, Perl or Python, which all have better ways.
"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment