git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 5/8] git-prompt: add some missing quotes

From
AGAvi Halachmi (:avih) via GitGitGadget <gitgitgadget@gmail.com>
Date
Aug 17, 2024, 09:25 UTC
Message-ID
<3a41ad889cc33a1fc0414b8f14af6438b49c88ee.1723886761.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.1750.v3.git.git.1723886760.gitgitgadget@gmail.com>
From: "Avi Halachmi (:avih)" <avihpit@yahoo.com>

The issues which this commit fixes are unlikely to be broken in real life, but the fixes improve correctness, and would prevent bugs in some uncommon cases, such as weird IFS values.

Listing some portability guideline here for future reference.

I'm leaving it to someone else to decide whether to include it in the file itself, place is as a new file, or not.

---------
The command "local" is non standard, but is allowed in this file:
- Quote initialization if it can expand (local x="$y"). See below.
- Don't assume initial value after "local x". Either initialize it
  (local x=..), or set before first use (local x;.. x=..; <use $x>).
  (between shells, "local x" can unset x, or inherit it, or do x= )
Other non-standard features beyond "local" are to be avoided.
Use the standard "test" - [...] instead of non-standard [[...]] .
--------
Quotes (some portability things, but mainly general correctness):
Quotes prevent tilde-expansion of some unquoted literal tildes (~).
If the expansion is undesirable, quotes would ensure that.
  Tilds expanded: a=~user:~/ ;  echo ~user ~/dir
  not expanded:   t="~"; a=${t}user  b=\~foo~;  echo "~user" $t/dir

But the main reason for quoting is to prevent IFS field splitting (which also coalesces IFS chars) and glob expansion in parts which contain parameter/arithmetic expansion or command substitution.

"Simple command" (POSIX term) is assignment[s] and/or command [args].
Examples:
  foo=bar         # one assignment
  foo=$bar x=y    # two assignments
  foo bar         # command, no assignments
  x=123 foo bar   # one assignment and a command
The assignments part is not IFS-split or glob-expanded.

The command+args part does get IFS field split and glob expanded, but only at unquoted expanded/substituted parts.

In the command+args part, expanded/substituted values must be quoted.
(the commands here are "[" and "local"):
  Good: [ "$mode" = yes ]; local s="*" x="$y" e="$?" z="$(cmd ...)"
  Bad:  [ $mode = yes ];   local s=*   x=$y   e=$?   z=$(cmd...)

The arguments to "local" do look like assignments, but they're not the assignment part of a simple command. they're at the command part.

Still at the command part, no need to quote non-expandable values:
  Good:                 local x=   y=yes;   echo OK
  OK, but not required: local x="" y="yes"; echo "OK"
But completely empty (NULL) arguments must be quoted:
  foo ""   is not the same as:   foo
Assignments in simple commands - with or without an actual command,
don't need quoting becase there's no IFS split or glob expansion:
  Good:   s=* a=$b c=$(cmd...)${x# foo }${y-   } [cmd ...]
  It's also OK to use double quotes, but not required.

This behavior (no IFS/glob) is called "assignment context", and "local" does not behave with assignment context in some shells, hence we require quotes when using "local" - for compatibility.

The value between 'case' and 'in' doesn't IFS-split/glob-expand:
  Good:       case  * $foo $(cmd...)  in ... ; esac
  identical:  case "* $foo $(cmd...)" in ... ; esac
Nested quotes in command substitution are fine, often necessary:
  Good: echo "$(foo... "$x" "$(bar ...)")"
Nested quotes in substring ops are legal, and sometimes needed
to prevent interpretation as a pattern, but not the most readable:
  Legal:  foo "${x#*"$y" }"
Nested quotes in "maybe other value" subst are invalid, unnecessary:
  Good:  local x="${y- }";   foo "${z:+ $a }"
  Bad:   local x="${y-" "}"; foo "${z:+" $a "}"
Outer/inner quotes in "maybe other value" have different use cases:
  "${x-$y}"  always one quoted arg: "$x" if x is set, else "$y".
  ${x+"$x"}  one quoted arg "$x" if x is set, else no arg at all.
  Unquoted $x is similar to the second case, but it would get split
  into few arguments if it includes any of the IFS chars.
Assignments don't need the outer quotes, and the braces delimit the
value, so nested quotes can be avoided, for readability:
  a=$(foo "$x")  a=${x#*"$y" }  c=${y- };  bar "$a" "$b" "$c"
Signed-off-by: Avi Halachmi (:avih) <avihpit@yahoo.com>
---
 contrib/completion/git-prompt.sh | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 4781261f868..5d7f236fe48 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -246,7 +246,7 @@ __git_ps1_show_upstream ()
 		if [ -n "$count" ] && [ -n "$name" ]; then
 			__git_ps1_upstream_name=$(git rev-parse \
 				--abbrev-ref "$upstream_type" 2>/dev/null)
-			if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+			if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
 				upstream="$upstream \${__git_ps1_upstream_name}"
 			else
 				upstream="$upstream ${__git_ps1_upstream_name}"
@@ -278,12 +278,12 @@ __git_ps1_colorize_gitstring ()
 		local c_lblue=$'\001\e[1;34m\002'
 		local c_clear=$'\001\e[0m\002'
 	fi
-	local bad_color=$c_red
-	local ok_color=$c_green
+	local bad_color="$c_red"
+	local ok_color="$c_green"
 	local flags_color="$c_lblue"
 
 	local branch_color=""
-	if [ $detached = no ]; then
+	if [ "$detached" = no ]; then
 		branch_color="$ok_color"
 	else
 		branch_color="$bad_color"
@@ -360,7 +360,7 @@ __git_sequencer_status ()
 __git_ps1 ()
 {
 	# preserve exit status
-	local exit=$?
+	local exit="$?"
 	local pcmode=no
 	local detached=no
 	local ps1pc_start='\u@\h:\w '
@@ -379,7 +379,7 @@ __git_ps1 ()
 		;;
 		0|1)	printf_format="${1:-$printf_format}"
 		;;
-		*)	return $exit
+		*)	return "$exit"
 		;;
 	esac
 
@@ -427,7 +427,7 @@ __git_ps1 ()
 	rev_parse_exit_code="$?"
 
 	if [ -z "$repo_info" ]; then
-		return $exit
+		return "$exit"
 	fi
 
 	local short_sha=""
@@ -449,7 +449,7 @@ __git_ps1 ()
 	   [ "$(git config --bool bash.hideIfPwdIgnored)" != "false" ] &&
 	   git check-ignore -q .
 	then
-		return $exit
+		return "$exit"
 	fi
 
 	local sparse=""
@@ -499,7 +499,7 @@ __git_ps1 ()
 			case "$ref_format" in
 			files)
 				if ! __git_eread "$g/HEAD" head; then
-					return $exit
+					return "$exit"
 				fi
 
 				case $head in
@@ -597,10 +597,10 @@ __git_ps1 ()
 		fi
 	fi
 
-	local z="${GIT_PS1_STATESEPARATOR-" "}"
+	local z="${GIT_PS1_STATESEPARATOR- }"
 
 	b=${b##refs/heads/}
-	if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
+	if [ "$pcmode" = yes ] && [ "$ps1_expanded" = yes ]; then
 		__git_ps1_branch_name=$b
 		b="\${__git_ps1_branch_name}"
 	fi
@@ -612,7 +612,7 @@ __git_ps1 ()
 	local f="$h$w$i$s$u$p"
 	local gitstring="$c$b${f:+$z$f}${sparse}$r${upstream}${conflict}"
 
-	if [ $pcmode = yes ]; then
+	if [ "$pcmode" = yes ]; then
 		if [ "${__git_printf_supports_v-}" != yes ]; then
 			gitstring=$(printf -- "$printf_format" "$gitstring")
 		else
@@ -623,5 +623,5 @@ __git_ps1 ()
 		printf -- "$printf_format" "$gitstring"
 	fi
 
-	return $exit
+	return "$exit"
 }
-- 
gitgitgadget
Previous: Avi Halachmi (:avih) via GitGitGadgetNext: Eric Sunshine
Message 61 of 80 in “git-prompt: support more shells”
  1. 0/8 git-prompt: support more shellsAvi Halachmi via GitGitGadget, Jul 23, 2024
  2. 1/8 git-prompt: use here-doc instead of here-stringAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  3. 2/8 git-prompt: fix uninitialized variableAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  4. 3/8 git-prompt: don't use shell arraysAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  5. 4/8 git-prompt: replace [[...]] with standard codeAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  6. 5/8 git-prompt: add some missing quotesAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  7. Junio C HamanoJul 23, 2024
  8. avihJul 24, 2024
  9. 6/8 git-prompt: add fallback for shells without $'...'Avi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  10. Junio C HamanoJul 23, 2024
  11. avihJul 24, 2024
  12. avihJul 25, 2024
  13. avihJul 25, 2024
  14. Junio C HamanoJul 25, 2024
  15. avihJul 25, 2024
  16. Junio C HamanoJul 25, 2024
  17. avihAug 14, 2024
  18. Junio C HamanoAug 14, 2024
  19. avihAug 15, 2024
  20. Junio C HamanoAug 15, 2024
  21. 7/8 git-prompt: ta-da! document usage in other shellsAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  22. 8/8 git-prompt: support custom 0-width PS1 markersAvi Halachmi (:avih) via GitGitGadget, Jul 23, 2024
  23. brian m. carlsonJul 23, 2024
  24. avihJul 24, 2024
  25. Junio C HamanoJul 24, 2024
  26. Junio C HamanoJul 24, 2024
  27. avihJul 24, 2024
  28. 0/8 git-prompt: support more shells v2Avi Halachmi via GitGitGadget, Aug 15, 2024
  29. 1/8 git-prompt: use here-doc instead of here-stringAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  30. Patrick SteinhardtAug 16, 2024
  31. avihAug 16, 2024
  32. Patrick SteinhardtAug 16, 2024
  33. 2/8 git-prompt: fix uninitialized variableAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  34. 3/8 git-prompt: don't use shell arraysAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  35. Patrick SteinhardtAug 16, 2024
  36. avihAug 16, 2024
  37. Patrick SteinhardtAug 16, 2024
  38. avihAug 16, 2024
  39. Patrick SteinhardtAug 16, 2024
  40. 4/8 git-prompt: replace [[...]] with standard codeAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  41. Junio C HamanoAug 15, 2024
  42. avihAug 16, 2024
  43. Junio C HamanoAug 16, 2024
  44. 5/8 git-prompt: add some missing quotesAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  45. Junio C HamanoAug 15, 2024
  46. avihAug 15, 2024
  47. Junio C HamanoAug 15, 2024
  48. avihAug 15, 2024
  49. 6/8 git-prompt: don't use shell $'...'Avi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  50. 7/8 git-prompt: ta-da! document usage in other shellsAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  51. Patrick SteinhardtAug 16, 2024
  52. avihAug 16, 2024
  53. 8/8 git-prompt: support custom 0-width PS1 markersAvi Halachmi (:avih) via GitGitGadget, Aug 15, 2024
  54. Junio C HamanoAug 15, 2024
  55. Patrick SteinhardtAug 16, 2024
  56. 0/8 git-prompt: support more shells v3Avi Halachmi via GitGitGadget, Aug 17, 2024
  57. 1/8 git-prompt: use here-doc instead of here-stringAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  58. 2/8 git-prompt: fix uninitialized variableAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  59. 3/8 git-prompt: don't use shell arraysAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  60. 4/8 git-prompt: replace [[...]] with standard codeAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  61. 5/8 git-prompt: add some missing quotesAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  62. Eric SunshineAug 17, 2024
  63. avihAug 17, 2024
  64. Junio C HamanoAug 17, 2024
  65. avihAug 17, 2024
  66. 6/8 git-prompt: don't use shell $'...'Avi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  67. 7/8 git-prompt: ta-da! document usage in other shellsAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  68. 8/8 git-prompt: support custom 0-width PS1 markersAvi Halachmi (:avih) via GitGitGadget, Aug 17, 2024
  69. 0/8 git-prompt: support more shells v4Avi Halachmi via GitGitGadget, Aug 20, 2024
  70. 1/8 git-prompt: use here-doc instead of here-stringAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  71. 2/8 git-prompt: fix uninitialized variableAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  72. 3/8 git-prompt: don't use shell arraysAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  73. 4/8 git-prompt: replace [[...]] with standard codeAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  74. 5/8 git-prompt: add some missing quotesAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  75. 6/8 git-prompt: don't use shell $'...'Avi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  76. 7/8 git-prompt: ta-da! document usage in other shellsAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  77. 8/8 git-prompt: support custom 0-width PS1 markersAvi Halachmi (:avih) via GitGitGadget, Aug 20, 2024
  78. Junio C HamanoAug 20, 2024
  79. avihAug 20, 2024
  80. avihAug 28, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.