Reputation: 1
I'm having trouble getting the length of a plain string in bash using #
(is there a common name for this operator? The reference manual calls it a special parameter, but there are many of those). More specifically, I want to get the length of a path with :
as a delimiter.
I've tried a few things, following the ${#parameter}
syntax found in the manual, but they all return an error.
length=${#":/path/to/a/directory"} # "bad substitution"
length=${#':/path/to/a/directory'} # "bad substitution"
length=${#:/path/to/a/directory} # "syntax error: operand expected (error token is "/path/to/a/directory")"
# Removing the `:`
length=${#"/path/to/a/directory"} # "bad substitution"
length=${#'/path/to/a/directory'} # "bad substitution"
length=${#/path/to/a/directory} # `length` is set to 0
Clearly, using a variable works.
var=":/path/to/a/directory"
length=${#var} # `length` is correctly set to 21
It seems evident that bash
does not support using #
on a literal string, but could there be a workaround to using a variable?
Upvotes: 0
Views: 628
Reputation: 37464
Using printf
:
$ printf -v_ "%s%n" ":/path/to/a/directory" length
$ echo $length
21
-v_ "%s"
to write the output to trash variable _
"%n" "string" length
to write the length of the string
to variable length
.
Upvotes: 0
Reputation: 204548
${#...}
is only for variables but with a literal string you can always do any of these (or various similar alternatives):
$ expr length ':/path/to/a/directory'
21
$ expr ':/path/to/a/directory' : '.*'
21
$ wc -c < <(printf '%s' ':/path/to/a/directory')
21
$ printf '%s' ':/path/to/a/directory' | wc -c
21
You can save the result of either command in a variable in the usual way, e.g.:
$ length=$( expr length ':/path/to/a/directory' )
$ echo "$length"
21
The only one above that's kinda cryptic IMO is expr ':/path/to/a/directory' : '.*'
- that's using expr
's :
operator to compare the original string ':/path/to/a/directory'
to the regex '.*'
, which of course matches the whole of that string, and then printing the number of characters that match, i.e. the number of characters in the original string.
Upvotes: 1
Reputation: 8209
You could define a function to do what you want. This Shellcheck-clean pure Bash code demonstrates one way to do it:
# Assign the length of parameter 2 to the variable named by parameter 1
function set_to_length
{
# Check parameter 1 is a valid variable name and parameter 2 is defined
[[ -z ${1-} ]] && return 1
[[ $1 == *[^[:alnum:]_]* ]] && return 1
[[ $1 == [[:digit:]]* ]] && return 1
[[ -z ${2+_} ]] && return 1
printf -v "$1" '%s' "${#2}"
}
set_to_length var :/path/to/a/directory
gives var
the value 21
.PATH
, for instance, so it still needs to be used carefully.printf -v
.Upvotes: 1
Reputation: 782488
${...}
is only for expanding variables, and performing transformations on the variable value in the process. So it can't be used for other expressions.
However, you can get the string length using command substitution.
length=$(printf "%s" ":/path/to/a/directory" | wc -c)
printf
is needed to avoid appending a newline to the string before piping to wc
, as it would count that character.
Upvotes: 1
Reputation: 141881
could there be a workaround to using a variable?
No, there is no workaround. Any variable expansions, like ${#
, work with variables, not strings.
is there a common name for this operator?
No, I do not think so. I call it "length operator" in my head. It would be really nice to have names for all those different expansions, they all are nameless.
length=${#/path/to/a/directory} # `length` is set to 0
The $#
variable is the special variable expanding to the number of arguments passed to a script. In the case of your shell, $#
is 0, your shell was invoked with no parameters. The ${#/path/to/a/directory}
is parsed as ${parameter/pattern/string}
case, where the parts are parameter=#
, pattern="path"
and string="to/a/directory"
.
Upvotes: 2