From 78abf0a86308639811d3faaae0322e9b2dd264ec Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Thu, 18 Jan 2024 16:53:26 +0000 Subject: [PATCH 01/10] wip --- .../reverse-string/.approaches/config.json | 0 .../.approaches/external-tools/content.md | 0 .../.approaches/external-tools/snippet.txt | 0 .../.approaches/introduction.md | 30 +++++++++++++++++++ .../.approaches/loops/content.md | 0 .../.approaches/loops/snippet.txt | 0 .../reverse-string/.articles/config.json | 0 .../.articles/performance/content.md | 0 .../.articles/performance/snippet.md | 0 9 files changed, 30 insertions(+) create mode 100644 exercises/practice/reverse-string/.approaches/config.json create mode 100644 exercises/practice/reverse-string/.approaches/external-tools/content.md create mode 100644 exercises/practice/reverse-string/.approaches/external-tools/snippet.txt create mode 100644 exercises/practice/reverse-string/.approaches/introduction.md create mode 100644 exercises/practice/reverse-string/.approaches/loops/content.md create mode 100644 exercises/practice/reverse-string/.approaches/loops/snippet.txt create mode 100644 exercises/practice/reverse-string/.articles/config.json create mode 100644 exercises/practice/reverse-string/.articles/performance/content.md create mode 100644 exercises/practice/reverse-string/.articles/performance/snippet.md diff --git a/exercises/practice/reverse-string/.approaches/config.json b/exercises/practice/reverse-string/.approaches/config.json new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.approaches/external-tools/content.md b/exercises/practice/reverse-string/.approaches/external-tools/content.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt b/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.approaches/introduction.md b/exercises/practice/reverse-string/.approaches/introduction.md new file mode 100644 index 00000000..38fa2879 --- /dev/null +++ b/exercises/practice/reverse-string/.approaches/introduction.md @@ -0,0 +1,30 @@ + + 167 str='Hello, World!' + 168 rev <<< $str + 169 grep -o . <<< $str + 170 grep -o . <<< $str | tac + 171 grep -o . <<< $str | tac | paste -s -d '' + 172 awk -F '' '{for (i = 1, j = NF; i < j; i++, j--) {tmp = $i; $i = $j; $j = tmp}; print $i}' <<< $str + 173 awk -F '' '{for (i = 1, j = NF; i < j; i++, j--) {tmp = $i; $i = $j; $j = tmp}; print}' <<< $str + 174 reverse() { local reversed=""; local i; for ((i = 0; i < ${#1}; i++)); do reversed=${1:i:1}$reversed; done; echo "$reversed"; } + 175 reverse "$str" + 176 set +x + 177 declare -f reverse + 178 set +x + 179 reverse "$str" + 180 reverse2 () { local reversed="" i; for ((i = ${#1} - 1; i >= 0; i--)); do reversed+=${1:i:1}; done; echo "$reversed"; } + 181 reverse "$str" + 182 reverse2 "$str" + 183 reverse3 () { local reversed="" char; while IFS= read -r -d '' char; do reversed=$char$reversed; done < <(printf '%s' "$1"); echo "$reversed"; } + 184 reverse3 "$str" + 185 reverse3 () { local reversed="" char; while IFS= read -r -d '' -n 1 char; do reversed=$char$reversed; done < <(printf '%s' "$1"); echo "$reversed"; } + 186 reverse3 "$str" + 187 printf '%s\n' one two three + 188 printf '%s\n' one two three | rev + 189 printf '%s\n' one two three | tac + 190 printf '%s\n' one two three | tac | rev + 191 echo "$str" | rev + 192 rev <<< "$str" + 193 grep -o . <<< "$str" + 194 grep -o . <<< "$str" | tac + 195 grep -o . <<< "$str" | tac | paste -s -d '' diff --git a/exercises/practice/reverse-string/.approaches/loops/content.md b/exercises/practice/reverse-string/.approaches/loops/content.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.approaches/loops/snippet.txt b/exercises/practice/reverse-string/.approaches/loops/snippet.txt new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.articles/config.json b/exercises/practice/reverse-string/.articles/config.json new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md new file mode 100644 index 00000000..e69de29b diff --git a/exercises/practice/reverse-string/.articles/performance/snippet.md b/exercises/practice/reverse-string/.articles/performance/snippet.md new file mode 100644 index 00000000..e69de29b From a9c2c3d002d5088f850344d47c6a7db79db4ff85 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sat, 20 Jan 2024 19:00:10 -0500 Subject: [PATCH 02/10] Reverse string approaches, and a Performance article --- .../reverse-string/.approaches/config.json | 28 +++++ .../.approaches/external-tools/content.md | 27 +++++ .../.approaches/external-tools/snippet.txt | 1 + .../.approaches/introduction.md | 78 +++++++++----- .../.approaches/loops/content.md | 43 ++++++++ .../.approaches/loops/snippet.txt | 4 + .../reverse-string/.articles/config.json | 13 +++ .../.articles/performance/content.md | 101 ++++++++++++++++++ .../.articles/performance/snippet.md | 5 + 9 files changed, 271 insertions(+), 29 deletions(-) diff --git a/exercises/practice/reverse-string/.approaches/config.json b/exercises/practice/reverse-string/.approaches/config.json index e69de29b..e1a07a00 100644 --- a/exercises/practice/reverse-string/.approaches/config.json +++ b/exercises/practice/reverse-string/.approaches/config.json @@ -0,0 +1,28 @@ +{ + "introduction": { + "authors": [ + "glennj" + ], + "contributors": [] + }, + "approaches": [ + { + "uuid": "351af57f-efd4-4a8b-967b-e635428937da", + "slug": "external-tools", + "title": "External tools", + "blurb": "Use external tools to reverse a string.", + "authors": [ + "glennj" + ] + }, + { + "uuid": "4363f61c-5caa-4b52-abe9-30ae671817fc", + "slug": "loops", + "title": "Looping", + "blurb": "Loop over the indices of the string.", + "authors": [ + "glennj" + ] + } + ] +} diff --git a/exercises/practice/reverse-string/.approaches/external-tools/content.md b/exercises/practice/reverse-string/.approaches/external-tools/content.md index e69de29b..35767bc5 100644 --- a/exercises/practice/reverse-string/.approaches/external-tools/content.md +++ b/exercises/practice/reverse-string/.approaches/external-tools/content.md @@ -0,0 +1,27 @@ +# External Tools + +The GNU Linux core utilities contain lots of goodies. +To reverse strings, use `rev`. + +```bash +$ echo "Hello, World!" | rev +!dlroW ,olleH +``` + +`rev` also works with files to reverse each line. + +```bash +$ printf '%s\n' one two three > myfile +$ rev myfile +eno +owt +eerht +``` + +There are other ways to do this, but none are a simple as `rev`. + +```bash +echo "$string" | grep -o . | tac | paste -s -d '' +echo "$string" | perl -lne 'print scalar reverse' +# etc +``` diff --git a/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt b/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt index e69de29b..7f7646f3 100644 --- a/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt +++ b/exercises/practice/reverse-string/.approaches/external-tools/snippet.txt @@ -0,0 +1 @@ +reversed=$( rev <<< "$string" ) diff --git a/exercises/practice/reverse-string/.approaches/introduction.md b/exercises/practice/reverse-string/.approaches/introduction.md index 38fa2879..20715c8f 100644 --- a/exercises/practice/reverse-string/.approaches/introduction.md +++ b/exercises/practice/reverse-string/.approaches/introduction.md @@ -1,30 +1,50 @@ +# Introduction - 167 str='Hello, World!' - 168 rev <<< $str - 169 grep -o . <<< $str - 170 grep -o . <<< $str | tac - 171 grep -o . <<< $str | tac | paste -s -d '' - 172 awk -F '' '{for (i = 1, j = NF; i < j; i++, j--) {tmp = $i; $i = $j; $j = tmp}; print $i}' <<< $str - 173 awk -F '' '{for (i = 1, j = NF; i < j; i++, j--) {tmp = $i; $i = $j; $j = tmp}; print}' <<< $str - 174 reverse() { local reversed=""; local i; for ((i = 0; i < ${#1}; i++)); do reversed=${1:i:1}$reversed; done; echo "$reversed"; } - 175 reverse "$str" - 176 set +x - 177 declare -f reverse - 178 set +x - 179 reverse "$str" - 180 reverse2 () { local reversed="" i; for ((i = ${#1} - 1; i >= 0; i--)); do reversed+=${1:i:1}; done; echo "$reversed"; } - 181 reverse "$str" - 182 reverse2 "$str" - 183 reverse3 () { local reversed="" char; while IFS= read -r -d '' char; do reversed=$char$reversed; done < <(printf '%s' "$1"); echo "$reversed"; } - 184 reverse3 "$str" - 185 reverse3 () { local reversed="" char; while IFS= read -r -d '' -n 1 char; do reversed=$char$reversed; done < <(printf '%s' "$1"); echo "$reversed"; } - 186 reverse3 "$str" - 187 printf '%s\n' one two three - 188 printf '%s\n' one two three | rev - 189 printf '%s\n' one two three | tac - 190 printf '%s\n' one two three | tac | rev - 191 echo "$str" | rev - 192 rev <<< "$str" - 193 grep -o . <<< "$str" - 194 grep -o . <<< "$str" | tac - 195 grep -o . <<< "$str" | tac | paste -s -d '' +To reverse a string in bash, there are two basic approaches. + +## Approach: External tools + +The most straightforward approach is to call out the `rev` utility. + +```bash +reversed=$( echo "$string" | rev ) +# or +reversed=$( rev <<< "$string" ) +``` + +For more details, see the [External tools approach][app-external]. + +## Approach: Loop over the string indices + +To reverse a string with just bash, loop over the indices, extract the character at that index, and add it to the accumulating result string. + +```bash +# forwards +reversed='' +for ((i = 0; i < ${#string}; i++)); do + reversed="${string:i:1}$reversed" +done + +# or backwards +reversed='' +for ((i = ${#string} - 1; i >= 0; i--)); do + reversed+="${string:i:1}" +done +``` + +For more details, go to the [Looping approach][app-loop]. + +## Which approach to use? + +Calling out to `rev` makes this exercise extremely trivial. +In a work environment, it's exactly the approach to take. + +If you're interested in learning about bash loops and parameter expansion, experimenting with the loop approach is more interesting. + +Thinking about performance generally isn't something you would care that much about with a shell script. +However, working with strings can be surprisingly expensive in bash. +The [Performance article][art-perf] takes a deeper dive. + +[app-external]: /tracks/bash/exercises/reverse-string/approaches/external-tools +[app-loop]: /tracks/bash/exercises/reverse-string/approaches/loops +[art-perf]: /tracks/bash/exercises/reverse-string/articles/performance diff --git a/exercises/practice/reverse-string/.approaches/loops/content.md b/exercises/practice/reverse-string/.approaches/loops/content.md index e69de29b..af103464 100644 --- a/exercises/practice/reverse-string/.approaches/loops/content.md +++ b/exercises/practice/reverse-string/.approaches/loops/content.md @@ -0,0 +1,43 @@ +# Looping + +In a bash loop, we are looping over the indices of the characters. +Bash strings and arrays use zero-based indexing. + +```bash +reversed='' +for ((i = 0; i < ${#string}; i++)); do + reversed="${string:i:1}$reversed" +done +``` + +- We loop from zero up to (but not including) the string length (`${#string}`). +- Extracting the character at index `i` is done with the `${var:offset:length}` parameter expansion. +- We _prepend_ the character to the accumulating variable to reverse the string. + +Finding the string length is a surprisingly expensive operation in bash (more details in the [Performance article][art-perf]). +We don't have to re-calculate it for every loop iteration, just do it once. + +```bash +reversed='' +len=${#string} +for ((i = 0; i < len; i++)); do + reversed="${string:i:1}$reversed" +done +``` + +An alternate way to calculate it just once is to loop backwards. + +```bash +reversed='' +for ((i = ${#string} - 1; i >= 0; i--)); do + reversed+="${string:i:1}" +done +``` + +- Here, we start the loop at one less than the string length, which is the index of the last character, and we loop down to (and including) zero. +- Since we're accessing the characters in the reverse order, we'll _append_ to the accumulating variable. + +Another performance note: accessing each character with this parameter expansion is still slow: bash has to walk the string until reaching the desired index. +The most efficient solution is discussed in the [Performance article][art-perf]). + +[art-perf]: /tracks/bash/exercises/reverse-string/articles/performance diff --git a/exercises/practice/reverse-string/.approaches/loops/snippet.txt b/exercises/practice/reverse-string/.approaches/loops/snippet.txt index e69de29b..89ae1d62 100644 --- a/exercises/practice/reverse-string/.approaches/loops/snippet.txt +++ b/exercises/practice/reverse-string/.approaches/loops/snippet.txt @@ -0,0 +1,4 @@ +reversed='' +for ((i = ${#string} - 1; i >= 0; i--)); do + reversed+="${string:i:1}" +done diff --git a/exercises/practice/reverse-string/.articles/config.json b/exercises/practice/reverse-string/.articles/config.json index e69de29b..7b80cc0c 100644 --- a/exercises/practice/reverse-string/.articles/config.json +++ b/exercises/practice/reverse-string/.articles/config.json @@ -0,0 +1,13 @@ +{ + "articles": [ + { + "uuid": "b8011164-e0fb-4fc3-ba8d-1b8a67f4ee4a", + "slug": "performance", + "title": "Performance considerations", + "blurb": "Compare the performances of the various reverse-string approaches.", + "authors": [ + "glennj" + ] + } + ] +} diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index e69de29b..8d6e4493 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -0,0 +1,101 @@ +# Performance + +We usually don't care that much about performance with shell scripts. +But when we can avoid doing something we know is slow, especially if this slow thing is done repeatedly inside a loop, avoid it. + +## String length is slow + +Obtaining the length of a string is a surprisingly expensive operation in bash. +With large strings and/or large loops, performance can be significantly impacted. +Storing the length in a variable helps significantly. + +Demonstrating an empty loop, iterating over the string indices. + +```bash +$ printf -v string "%32767s" foo +$ time for ((i = 0; i < ${#string}; i++)); do true; done + +real 0m1.448s +user 0m1.443s +sys 0m0.005s + +$ len=${#string} +$ time for ((i = 0; i < len; i++)); do :; done + +real 0m0.159s +user 0m0.159s +sys 0m0.000s +``` + +If we can loop backwards, don't even need the variable. +We even get a tiny improvement since bash does not need to access the variable contents for each iteration. + +```bash +$ time for ((i = ${#string} - 1; i >= 0; i--)); do true; done + +real 0m0.151s +user 0m0.151s +sys 0m0.000s +``` + +## Substrings can be slow + +Whether we go backwards or forwards, we are still having to extract the character at the given index. +Re-using our 32,767 character string: + +```bash +$ time for ((i = 0; i < len; i++)); do + char="${string:i:1}" + done + +real 0m9.188s +user 0m9.188s +sys 0m0.000s +``` + +That's 9 seconds just to read each character. +What if we can iterate over the characters directly? +We can, with a while-read loop. + +## While-Read loops + +We'll _redirect_ the string into loop so `read` can extract one character at a time. + +```bash +$ time while IFS= read -d "" -r -n 1 char; do + true + done < <(printf "%s" "$string") + +real 0m0.336s +user 0m0.276s +sys 0m0.060s +``` + +There's a 27x improvement. + +What are we doing there? + +- `< <(printf "%s" "%string)` is redirecting (`<`) a [process substitution][process-subst] (`<(...)`). + We could use a [here-string][here-string] (`<<< "$string"`), but that appends a newline. + Using `printf` like that redirects the string exactly as it is. +- `IFS= read -d "" -r -n 1 char` OK there's a lot going on there with this [`read` command][read]: + - `IFS=`: Normally, `read` will trim leading and trailing whitespace. + More exactly, characters in `$IFS` are removed from the start and end of the text that `read` captures. + By default, IFS contains space, tab and newline. + Our goal is to read each character of the string, including whitespace. + That empty assignment assigns the empty string to IFS _only for the duration of the `read` command_ so that whitespace characters are not treated specially. + - `-d ""`: Normally, `read` will read up to a newline and stop there. + We typically use a while-read loop to read lines from a file. + But here, we want every character in the string including newlines. + Bash uses null terminated strings under the hood, so a bash string cannot contain a null character. + `-d ""` sets the line-ending delimiter to the null character. + - `-r`: Backslashes are handled specially by `read`. + We want to handle backslashes just like a normal character. + - `-n 1`: This limits `read` to take just one character from the input. + +Bash is strongly optimized for reading from and writing to IO streams. +Although this while-read loop is ugly, it's the fastest way for bash to process text one character at a time. + +[process-subst]: https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution +[here-string]: https://www.gnu.org/software/bash/manual/bash.html#Here-Strings +[read]: https://www.gnu.org/software/bash/manual/bash.html#index-read diff --git a/exercises/practice/reverse-string/.articles/performance/snippet.md b/exercises/practice/reverse-string/.articles/performance/snippet.md index e69de29b..839d0912 100644 --- a/exercises/practice/reverse-string/.articles/performance/snippet.md +++ b/exercises/practice/reverse-string/.articles/performance/snippet.md @@ -0,0 +1,5 @@ +```bash +while IFS= read -d "" -r -n 1 char; do + echo "$char" +done < <(printf "%s" "$string") +``` From 30a6f40e08e69664d3dde736481b22aa578eb71b Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:19:26 -0500 Subject: [PATCH 03/10] Update exercises/practice/reverse-string/.approaches/introduction.md Co-authored-by: Isaac Good --- exercises/practice/reverse-string/.approaches/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.approaches/introduction.md b/exercises/practice/reverse-string/.approaches/introduction.md index 20715c8f..0a4f1987 100644 --- a/exercises/practice/reverse-string/.approaches/introduction.md +++ b/exercises/practice/reverse-string/.approaches/introduction.md @@ -37,7 +37,7 @@ For more details, go to the [Looping approach][app-loop]. ## Which approach to use? Calling out to `rev` makes this exercise extremely trivial. -In a work environment, it's exactly the approach to take. +In a production environment, it's exactly the right approach to take. If you're interested in learning about bash loops and parameter expansion, experimenting with the loop approach is more interesting. From b04fcb2a8d20938cfb9c1a47cfcd6079e1230fc3 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:19:45 -0500 Subject: [PATCH 04/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index 8d6e4493..a0d5a64d 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -1,7 +1,8 @@ # Performance We usually don't care that much about performance with shell scripts. -But when we can avoid doing something we know is slow, especially if this slow thing is done repeatedly inside a loop, avoid it. +When we know something is slow, we can avoid it. +This matters much more when the operation is done inside loop. ## String length is slow From fb0f9c3619b506d84d7655879560b29b0078d2bf Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:19:55 -0500 Subject: [PATCH 05/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index a0d5a64d..dfa915b4 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -28,7 +28,7 @@ user 0m0.159s sys 0m0.000s ``` -If we can loop backwards, don't even need the variable. +If we can loop backwards, we don't even need to save the length to a variable. We even get a tiny improvement since bash does not need to access the variable contents for each iteration. ```bash From a227264afb81054ba294551f348fbd65b7ed50ad Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:20:02 -0500 Subject: [PATCH 06/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index dfa915b4..eb60e695 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -29,7 +29,7 @@ sys 0m0.000s ``` If we can loop backwards, we don't even need to save the length to a variable. -We even get a tiny improvement since bash does not need to access the variable contents for each iteration. +We get a tiny improvement since bash does not need to access the variable contents for each iteration. ```bash $ time for ((i = ${#string} - 1; i >= 0; i--)); do true; done From cba72bffd42c9fdad22b890597b0740810fdee6c Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:20:09 -0500 Subject: [PATCH 07/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index eb60e695..e69e2434 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -41,7 +41,7 @@ sys 0m0.000s ## Substrings can be slow -Whether we go backwards or forwards, we are still having to extract the character at the given index. +Whether we go backwards or forwards, we still have to extract the character at the given index. Re-using our 32,767 character string: ```bash From 597c5780d6301eb5d6b7767f0b5f6f406ce6a578 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:20:27 -0500 Subject: [PATCH 08/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index e69e2434..61ca5bb3 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -78,7 +78,7 @@ What are we doing there? - `< <(printf "%s" "%string)` is redirecting (`<`) a [process substitution][process-subst] (`<(...)`). We could use a [here-string][here-string] (`<<< "$string"`), but that appends a newline. - Using `printf` like that redirects the string exactly as it is. + Using `printf` outputs the string without adding a trailing newline. - `IFS= read -d "" -r -n 1 char` OK there's a lot going on there with this [`read` command][read]: - `IFS=`: Normally, `read` will trim leading and trailing whitespace. More exactly, characters in `$IFS` are removed from the start and end of the text that `read` captures. From d36bfba57ea19930f04bad0a1ea5ef01e96a19b1 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:20:45 -0500 Subject: [PATCH 09/10] Update exercises/practice/reverse-string/.articles/performance/content.md Co-authored-by: Isaac Good --- .../practice/reverse-string/.articles/performance/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index 61ca5bb3..8888ab1f 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -84,7 +84,7 @@ What are we doing there? More exactly, characters in `$IFS` are removed from the start and end of the text that `read` captures. By default, IFS contains space, tab and newline. Our goal is to read each character of the string, including whitespace. - That empty assignment assigns the empty string to IFS _only for the duration of the `read` command_ so that whitespace characters are not treated specially. + The empty assignment assigns the empty string to IFS _only for the duration of the `read` command_ so that whitespace characters are not treated specially. - `-d ""`: Normally, `read` will read up to a newline and stop there. We typically use a while-read loop to read lines from a file. But here, we want every character in the string including newlines. From 2881ac1d338fe30eabe0c5eac38c68139d4fe66e Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sun, 21 Jan 2024 12:39:10 -0500 Subject: [PATCH 10/10] review suggestions --- .../reverse-string/.approaches/external-tools/content.md | 4 ++-- .../practice/reverse-string/.articles/performance/content.md | 2 +- .../practice/reverse-string/.articles/performance/snippet.md | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exercises/practice/reverse-string/.approaches/external-tools/content.md b/exercises/practice/reverse-string/.approaches/external-tools/content.md index 35767bc5..92bea26b 100644 --- a/exercises/practice/reverse-string/.approaches/external-tools/content.md +++ b/exercises/practice/reverse-string/.approaches/external-tools/content.md @@ -21,7 +21,7 @@ eerht There are other ways to do this, but none are a simple as `rev`. ```bash -echo "$string" | grep -o . | tac | paste -s -d '' -echo "$string" | perl -lne 'print scalar reverse' +grep -o . <<< "$string" | tac | paste -s -d '' +perl -lne 'print scalar reverse' <<< "$string" # etc ``` diff --git a/exercises/practice/reverse-string/.articles/performance/content.md b/exercises/practice/reverse-string/.articles/performance/content.md index 8888ab1f..71ecfba1 100644 --- a/exercises/practice/reverse-string/.articles/performance/content.md +++ b/exercises/practice/reverse-string/.articles/performance/content.md @@ -79,7 +79,7 @@ What are we doing there? - `< <(printf "%s" "%string)` is redirecting (`<`) a [process substitution][process-subst] (`<(...)`). We could use a [here-string][here-string] (`<<< "$string"`), but that appends a newline. Using `printf` outputs the string without adding a trailing newline. -- `IFS= read -d "" -r -n 1 char` OK there's a lot going on there with this [`read` command][read]: +- `IFS= read -d "" -r -n 1 char`: There's a lot going on there with this [`read` command][read]. - `IFS=`: Normally, `read` will trim leading and trailing whitespace. More exactly, characters in `$IFS` are removed from the start and end of the text that `read` captures. By default, IFS contains space, tab and newline. diff --git a/exercises/practice/reverse-string/.articles/performance/snippet.md b/exercises/practice/reverse-string/.articles/performance/snippet.md index 839d0912..4214a467 100644 --- a/exercises/practice/reverse-string/.articles/performance/snippet.md +++ b/exercises/practice/reverse-string/.articles/performance/snippet.md @@ -1,5 +1,6 @@ ```bash while IFS= read -d "" -r -n 1 char; do + # do something with $char echo "$char" done < <(printf "%s" "$string") ```